【问题标题】:Evaluate a string with a switch in C++ [duplicate]在 C++ 中使用开关评估字符串 [重复]
【发布时间】:2013-04-29 14:30:09
【问题描述】:

我想用开关评估一个字符串,但是当我读取用户输入的字符串时,我会抛出以下错误。

#include<iostream>
using namespace std;

    int main() {
        string a;
        cin>>a;
        switch (string(a)) {
        case "Option 1":
            cout<<"It pressed number 1"<<endl;
            break;
        case "Option 2":
            cout<<"It pressed number 2"<<endl;
            break;
        case "Option 3":
            cout<<"It pressed number 3"<<endl;
            break;
        default:
            cout<<"She put no choice"<<endl;
            break;
        }
        return 0;
    }

错误:从类型“std::string {aka std::basic_string}”到类型“int”的无效转换

【问题讨论】:

  • std::string 不适用于 switch。
  • Switch 表达式的计算结果必须为整数类型。
  • hash the string 如果你真的想要的话。 Some hashing algorithm。或者你能不能只得到选项编号,即 1、2、3、...?
  • if (a &gt;= "Option 1" &amp;&amp; a &lt;= "Option 3") {std::cout &lt;&lt; "It pressed number " + std::string(a.rbegin(), a.rbegin() + 1) &lt;&lt; '\n';} else {std::cout &lt;&lt; "She put no choice\n";}
  • @antitrust:不,同样,try/catch 不会保护 UB。在访问字符之前,您需要使用 if 测试长度。或者您是否在考虑边界检查的 at() 方法而不是未检查的 operator[]()

标签: c++


【解决方案1】:

如前所述,switch 只能用于整数值。因此,您只需要将“case”值转换为整数。你可以通过c++11中的constexpr来实现,这样一些constexpr函数的调用可以在编译时计算出来。

类似的...

switch (str2int(s))
{
  case str2int("Value1"):
    break;
  case str2int("Value2"):
    break;
}

str2int 是这样的(从here 实现):

constexpr unsigned int str2int(const char* str, int h = 0)
{
    return !str[h] ? 5381 : (str2int(str, h+1) * 33) ^ str[h];
}

再举个例子,可以在编译时计算下一个函数:

constexpr int factorial(int n)
{
    return n <= 1 ? 1 : (n * factorial(n-1));
}  

int f5{factorial(5)};
// Compiler will run factorial(5) 
// and f5 will be initialized by this value. 
// so programm instead of wasting time for running function, 
// just will put the precalculated constant to f5 

【讨论】:

  • 编译器可能会这样做,也可能不会。如果你发了f5constexpr,那就必须这样做。
  • 这正是我讨厌使用 c++ 的原因。它在很多方面都违反了最小意外原则。这是其中之一。
  • 小心只使用哈希来测试字符串是否相等。例如,对于这个str2int() 函数,str2int("WS") == str2int("tP")str2int("5g") == str2int("sa")。请参阅dmytry.blogspot.com/2009/11/horrible-hashes.html 尽管您实际比较的字符串之间的哈希冲突不太可能发生,但我认为最好使用将字符串转换为枚举的查找表(如 mskfisher 的回答中所建议的那样)。
  • str2int(s) 应该是 str2int(s.c_str()) 如果 s 是 std::string。一个好的解决方案是创建另一个接受 std::strings 但不是 constexpr 的 str2int 函数
  • @schlebe 是的,我明白了,老实说,我不能说哪个哈希函数更好,这个答案的目的是展示如何使用 constexpr 来解决这个问题。在任何情况下,如果此哈希函数对switch 块中的不同字符串给出相同的结果,编译器都会发出错误。
【解决方案2】:

您可以将字符串映射到枚举值,然后打开枚举:

enum Options {
    Option_Invalid,
    Option1,
    Option2,
    //others...
};

Options resolveOption(string input);

//  ...later...

switch( resolveOption(input) )
{
    case Option1: {
        //...
        break;
    }
    case Option2: {
        //...
        break;
    }
    // handles Option_Invalid and any other missing/unmapped cases
    default: {
        //...
        break;
    }
}

解析枚举可以实现为一系列if检查:

 Options resolveOption(std::string input) {
    if( input == "option1" ) return Option1;
    if( input == "option2" ) return Option2;
    //...
    return Option_Invalid;
 }

或者地图查询:

 Options resolveOption(std::string input) {
    static const std::map<std::string, Option> optionStrings {
        { "option1", Option1 },
        { "option2", Option2 },
        //...
    };

    auto itr = optionStrings.find(input);
    if( itr != optionStrings.end() ) {
        return itr->second;
    }
    return Option_Invalid; 
}

【讨论】:

  • resolveOption 是另一个函数还是标准库中的函数?
  • resolveOption是自定义函数,根据当前程序输入计算枚举器Options的值,
  • 为清楚起见,我添加了resolveOption() 函数的两种可能实现。
  • 最后,通过这个解决方案,用 if/else 语句替换 switch 似乎真的更快。事实上,您在函数 resolveOption 的第一个版本中就是这样做的。
  • 谢谢,@coincoin - 已修复。
【解决方案3】:

switch 语句只能用于整数值,不能用于用户定义类型的值。 (即使可以,您的输入操作也不起作用。&gt;&gt; 操作提取单个标记,由空格分隔,因此它永远无法检索值 "Option 1"。)

你可能想要这个:

#include <string>
#include <iostream>


std::string input;

if (!std::getline(std::cin, input)) { /* error, abort! */ }

if (input == "Option 1")
{
    // ... 
}
else if (input == "Option 2")
{ 
   // ...
}

// etc.

【讨论】:

    【解决方案4】:

    您只能在可转换为 int 的类型上使用 switch-case。

    但是,您可以定义一个 std::map&lt;std::string, std::function&gt; dispatcher 并像 dispatcher[str]() 一样使用它来达到相同的效果。

    【讨论】:

    【解决方案5】:

    你不能。句号。

    switch 仅适用于整数类型,如果要根据字符串进行分支,则需要使用if/else

    【讨论】:

      【解决方案6】:

      只要有选项号怎么样:

      #include <iostream>
      #include <string>
      using namespace std;
      
      int main()
      {
          string s;
          int op;
      
          cin >> s >> op;
          switch (op) {
          case 1: break;
          case 2: break;
          default:
          }
      
          return 0;
      }  
      

      【讨论】:

        【解决方案7】:

        开关值必须是整数类型。此外,由于您知道区分字符在位置7,您可以打开a.at(7)。但是您不确定用户输入了 8 个字符。他也可能犯了一些打字错误。所以你要在 Try Catch 中包围你的 switch 语句。有这种味道的东西

        #include<iostream>
        using namespace std;
        int main() {
            string a;
            cin>>a;
        
            try
            {
            switch (a.at(7)) {
            case '1':
                cout<<"It pressed number 1"<<endl;
                break;
            case '2':
                cout<<"It pressed number 2"<<endl;
                break;
            case '3':
                cout<<"It pressed number 3"<<endl;
                break;
            default:
                cout<<"She put no choice"<<endl;
                break;
            }
            catch(...)
            {
        
            }
            }
            return 0;
        }
        

        switch 语句中的 default 子句捕获用户输入至少为 8 个字符但不在 {1,2,3} 中的情况。

        或者,您可以打开enum 中的值。

        编辑

        使用operator[]() 获取第 7 个字符不会执行边界检查,因此该行为将是未定义的。我们使用来自std::stringat(),这是经过边界检查的as explained here.

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-04-20
          • 1970-01-01
          • 2013-06-19
          • 2011-08-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-02-29
          相关资源
          最近更新 更多