为什么switch语句不能应用于string?

int main() { switch(std::string("raj")) //Compilation error - switch expression of type illegal { case"sda": } } 

之所以要与types系统有关。 C / C ++并不真正支持string作为一种types。 它确实支持一个常量字符数组的概念,但它并不完全理解string的概念。

为了生成switch语句的代码,编译器必须明白两个值相等的含义。 对于像int和枚举这样的项目,这是一个微不足道的比较。 但是,编译器应该如何比较两个string值呢? 区分大小写,不区分大小写,文化意识等…如果没有对string的完整意识,就无法准确回答。

另外,C / C ++ switch语句通常以分支表的forms生成。 生成string样式转换的分支表并不容易。

如前所述,编译器喜欢构build查找表,尽可能地将switch语句优化为接近O(1)的时间。 将此与C ++语言没有stringtypes的事实相结合 – std::string是标准库的一部分,它不是语言本身的一部分。

我会提供一个替代scheme,你可能会考虑,我过去使用它的效果很好。 而不是切换string本身,切换使用该string作为input的散列函数的结果。 如果使用预先确定的一组string,那么您的代码几乎和切换string一样清晰:

 enum string_code { eFred, eBarney, eWilma, eBetty, ... }; string_code hashit (std::string const& inString) { if (inString == "Fred") return eFred; if (inString == "Barney") return eBarney; ... } void foo() { switch (hashit(stringValue)) { case eFred: ... case eBarney: ... } } 

有一些显而易见的优化,几乎遵循C编译器会用switch语句做什么……有趣的是如何发生的。

你只能使用像int,char和enum这样的原始开关。 最简单的解决方法就是使用枚举。

 #include <map> #include <string> #include <iostream.h> // Value-Defintions of the different String values static enum StringValue { evNotDefined, evStringValue1, evStringValue2, evStringValue3, evEnd }; // Map to associate the strings with the enum values static std::map<std::string, StringValue> s_mapStringValues; // User input static char szInput[_MAX_PATH]; // Intialization static void Initialize(); int main(int argc, char* argv[]) { // Init the string map Initialize(); // Loop until the user stops the program while(1) { // Get the user's input cout << "Please enter a string (end to terminate): "; cout.flush(); cin.getline(szInput, _MAX_PATH); // Switch on the value switch(s_mapStringValues[szInput]) { case evStringValue1: cout << "Detected the first valid string." << endl; break; case evStringValue2: cout << "Detected the second valid string." << endl; break; case evStringValue3: cout << "Detected the third valid string." << endl; break; case evEnd: cout << "Detected program end command. " << "Programm will be stopped." << endl; return(0); default: cout << "'" << szInput << "' is an invalid string. s_mapStringValues now contains " << s_mapStringValues.size() << " entries." << endl; break; } } return 0; } void Initialize() { s_mapStringValues["First Value"] = evStringValue1; s_mapStringValues["Second Value"] = evStringValue2; s_mapStringValues["Third Value"] = evStringValue3; s_mapStringValues["end"] = evEnd; cout << "s_mapStringValues contains " << s_mapStringValues.size() << " entries." << endl; } 

代码 Stefan Ruck于2001年7月25日撰写 。

问题是出于优化的原因,C ++中的switch语句不能用于基本types以外的任何东西,只能将它们与编译时常量进行比较。

推测这个限制的原因是编译器能够应用某种forms的优化,将代码编译成一个cmp指令,并在运行时根据参数的值计算出地址。 由于分支和循环不能很好地与现代CPU搭配使用,这可能是一个重要的优化。

为了解决这个问题,恐怕你不得不求助于陈述。

上面显然不是@MarmouCorp的C ++ 11更新,但是http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4067/Switch-on-Strings-in-C.htm

使用两个映射在string和类枚举之间进行转换(比普通枚举更好,因为它的值在其范围内,反向查找好的错误消息)。

编译器支持初始化器列表,这意味着VS 2013 plus可以在codeguru代码中使用static。 海湾合作委员会4.8.1是可以的,不知道多远可以兼容。

 /// <summary> /// Enum for String values we want to switch on /// </summary> enum class TestType { SetType, GetType }; /// <summary> /// Map from strings to enum values /// </summary> std::map<std::string, TestType> MnCTest::s_mapStringToTestType = { { "setType", TestType::SetType }, { "getType", TestType::GetType } }; /// <summary> /// Map from enum values to strings /// </summary> std::map<TestType, std::string> MnCTest::s_mapTestTypeToString { {TestType::SetType, "setType"}, {TestType::GetType, "getType"}, }; 

 std::string someString = "setType"; TestType testType = s_mapStringToTestType[someString]; switch (testType) { case TestType::SetType: break; case TestType::GetType: break; default: LogError("Unknown TestType ", s_mapTestTypeToString[testType]); } 

在C ++和C开关中只能使用整数types。 使用if else阶梯。 C ++显然可以实现某种string的swich语句 – 我想没有人认为这是值得的,我同意他们。

std::map + C ++ 11 lambda

我喜欢这个,因为不需要额外的枚举,它应该给我们O(log(N)

 #include <cassert> #include <cmath> #include <functional> #include <map> int main() { double result; auto m = std::map<std::string,std::function<void(double)>> { {"inv", [&](double x){ result = -x; }}, {"sqrt", [&](double x){ result = std::sqrt(x); }}, {"pow2", [&](double x){ result = x * x; }}, }; m["inv"](4.0); assert(result == -4.0); m["sqrt"](4.0); assert(result == 2.0); m["pow2"](4.0); assert(result == 16.0); } 

还要考虑O(1) std::unordered_map : 在C ++中使用HashMap的最佳方式是什么?

在C ++中,只能在int和char上使用switch语句

我认为原因在于Cstring不是原始types,正如tomjen所说的,把一个string当作一个char数组,所以你不能这样做:

 switch (char[]) { // ... switch (int[]) { // ... 

为什么不? 您可以使用开关实现等效的语法和相同的语义。 C语言根本没有对象和string对象,但C中的string是由指针引用的空string。 C++语言有可能为对象比较或检查对象平等而做出重载函数。 就像C C++一样, C++足够灵活地为C语言的string和任何支持C++语言的比较或检查相等的任何types的对象进行切换。 而现代C++11允许这个开关实现足够有效。

你的代码将是这样的:

 std::string name = "Alice"; std::string gender = "boy"; std::string role; SWITCH(name) CASE("Alice") FALL CASE("Carol") gender = "girl"; FALL CASE("Bob") FALL CASE("Dave") role = "participant"; BREAK CASE("Mallory") FALL CASE("Trudy") role = "attacker"; BREAK CASE("Peggy") gender = "girl"; FALL CASE("Victor") role = "verifier"; BREAK DEFAULT role = "other"; END // the role will be: "participant" // the gender will be: "girl" 

可以使用更复杂的types,例如std::pairs或任何支持相等操作的结构或类(或快速模式的同名)。

特征

  • 任何支持比较或检查相等的数据types
  • build立级联嵌套开关状态的可能性。
  • 可能性通过案例陈述来破坏或破坏
  • 可能性使用非constatnt caseexpression式
  • 可以使用树search来启用快速静态/dynamic模式(对于C ++ 11)

Sintax与语言切换的区别在于

  • 大写关键字
  • CASE语句需要括号
  • 分号“;” 在声明的结尾是不允许的
  • 冒号':'CASE语句是不允许的
  • 在CASE语句结束时需要BREAK或FALL关键字之一

对于C++97语言使用线性search。 对于C++11和更现代的可能使用quick模式的Wuth树search,CASE中的返回语句变得不被允许。 C语言实现存在于使用char*types和零终止string比较的地方。

阅读更多关于这个开关实现。

在c ++中,string不是头等公民。 string操作通过标准库完成。 我想,这就是原因。 而且,C ++使用分支表优化来优化switch case语句。 看看链接。

http://en.wikipedia.org/wiki/Switch_statement

使用最简单的容器添加一个变体(不需要一个有序的映射)…我不会打扰一个枚举 – 只需将容器的定义放在切换开关之前,这样就很容易看出哪个数字表示这种情况下。

这在unordered_map中进行哈希查找,并使用关联的int来驱动switch语句。 应该相当快。 请注意,使用at而不是[] ,因为我已经包含了const 。 使用[]可能是危险的 – 如果string不在地图中,您将创build一个新的地图,并可能会导致未定义的结果或不断增长的地图。

请注意,如果string不在地图中, at()函数将会引发exception。 所以你可能要先用count()来testing。

 const static std::unordered_map<std::string,int> string_to_case{ {"raj",1}, {"ben",2} }; switch(string_to_case.at("raj")) { case 1: // this is the "raj" case break; case 2: // this is the "ben" case break; } 

具有未定义stringtesting的版本如下:

 const static std::unordered_map<std::string,int> string_to_case{ {"raj",1}, {"ben",2} }; switch(string_to_case.count("raj") ? string_to_case.at("raj") : 0) { case 1: // this is the "raj" case break; case 2: // this is the "ben" case break; case 0: //this is for the undefined case } 

你不能在switch case中使用string,只允许int&char。 相反,你可以尝试使用枚举来表示string,并将其用在switch case块中

 enum MyString(raj,taj,aaj); 

在swich case语句中使用它。

开关只适用于整型(int,char,bool等)。 为什么不使用地图将一个string与一个数字配对,然后在开关上使用该数字?

  cout << "\nEnter word to select your choice\n"; cout << "ex to exit program (0)\n"; cout << "m to set month(1)\n"; cout << "y to set year(2)\n"; cout << "rm to return the month(4)\n"; cout << "ry to return year(5)\n"; cout << "pc to print the calendar for a month(6)\n"; cout << "fdc to print the first day of the month(1)\n"; cin >> c; cout << endl; a = c.compare("ex") ?c.compare("m") ?c.compare("y") ? c.compare("rm")?c.compare("ry") ? c.compare("pc") ? c.compare("fdc") ? 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0; switch (a) { case 0: return 1; case 1: ///m { cout << "enter month\n"; cin >> c; cout << endl; myCalendar.setMonth(c); break; } case 2: cout << "Enter year(yyyy)\n"; cin >> y; cout << endl; myCalendar.setYear(y); break; case 3: myCalendar.getMonth(); break; case 4: myCalendar.getYear(); case 5: cout << "Enter month and year\n"; cin >> c >> y; cout << endl; myCalendar.almanaq(c,y); break; case 6: break; } 

在许多情况下,您可以通过从string中拉出第一个字符并开启它来避免额外的工作。 如果您的案例以相同的值开始,可能最终不得不在charat(1)上进行嵌套切换。 任何人阅读你的代码将欣赏一个提示,因为大多数人会只是如果,否则,如果

C ++

constexpr散列函数:

 constexpr unsigned int hash(const char *s, int off = 0) { return !s[off] ? 5381 : (hash(s, off+1)*33) ^ s[off]; } switch( hash(str) ){ case hash("one") : // do something case hash("two") : // do something } 

这是因为C ++把开关转换成跳转表。 它对input数据执行一个简单的操作,并跳转到正确的地址而不进行比较。 由于string不是一个数字,而是一个数字数组,C ++不能从中创build一个跳转表。

 movf INDEX,W ; move the index value into the W (working) register from memory addwf PCL,F ; add it to the program counter. each PIC instruction is one byte ; so there is no need to perform any multiplication. ; Most architectures will transform the index in some way before ; adding it to the program counter table ; the branch table begins here with this label goto index_zero ; each of these goto instructions is an unconditional branch goto index_one ; of code goto index_two goto index_three index_zero ; code is added here to perform whatever action is required when INDEX = zero return index_one ... 

(代码来自维基百科https://en.wikipedia.org/wiki/Branch_table