C++ 为什么 switch 语句不能应用于字符串?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/650162/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 16:30:22  来源:igfitidea点击:

Why the switch statement cannot be applied on strings?

c++stringswitch-statement

提问by yesraaj

Compiling the following code and got the error of type illegal.

编译以下代码并得到type illegal.

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

You cannot use string in either switchor case. Why? Is there any solution that works nicely to support logic similar to switch on strings?

您不能在switch或 中使用字符串case。为什么?是否有任何解决方案可以很好地支持类似于打开字符串的逻辑?

采纳答案by JaredPar

The reason why has to do with the type system. C/C++ doesn't really support strings as a type. It does support the idea of a constant char array but it doesn't really fully understand the notion of a string.

原因与类型系统有关。C/C++ 并不真正支持字符串作为一种类型。它确实支持常量字符数组的想法,但它并没有真正完全理解字符串的概念。

In order to generate the code for a switch statement the compiler must understand what it means for two values to be equal. For items like ints and enums, this is a trivial bit comparison. But how should the compiler compare 2 string values? Case sensitive, insensitive, culture aware, etc ... Without a full awareness of a string this cannot be accurately answered.

为了生成 switch 语句的代码,编译器必须理解两个值相等意味着什么。对于整数和枚举之类的项目,这是一个微不足道的比较。但是编译器应该如何比较 2 个字符串值呢?区分大小写、不敏感、文化意识等……如果不完全了解字符串,则无法准确回答。

Additionally, C/C++ switch statements are typically generated as branch tables. It's not nearly as easy to generate a branch table for a string style switch.

此外,C/C++ switch 语句通常作为分支表生成。为字符串样式开关生成分支表并不那么容易。

回答by D.Shawley

As mentioned previously, compilers like to build lookup tables that optimize switchstatements to near O(1) timing whenever possible. Combine this with the fact that the C++ Language doesn't have a string type - std::stringis part of the Standard Library which is not part of the Language per se.

如前所述,编译器喜欢构建查找表,switch尽可能将语句优化到接近 O(1) 的时序。将此与 C++ 语言没有字符串类型的事实相结合 -std::string是标准库的一部分,而不是语言本身的一部分。

I will offer an alternative that you might want to consider, I've used it in the past to good effect. Instead of switching over the string itself, switch over the result of a hash function that uses the string as input. Your code will be almost as clear as switching over the string if you are using a predetermined set of strings:

我将提供您可能想要考虑的替代方案,我过去曾使用过它,效果很好。不是切换字符串本身,而是切换使用字符串作为输入的哈希函数的结果。如果您使用一组预先确定的字符串,您的代码将几乎与切换字符串一样清晰:

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:
        ...
    }
}

There are a bunch of obvious optimizations that pretty much follow what the C compiler would do with a switch statement... funny how that happens.

有一堆明显的优化几乎遵循 C 编译器对 switch 语句所做的事情……有趣的是,这是如何发生的。

回答by MarmouCorp

You can only use switch on primitive such as int, char and enum. The easiest solution to do it like you want to, is to use an enum.

您只能在 int、char 和 enum 等原语上使用 switch。像您想要的那样做的最简单的解决方案是使用枚举。

#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;
}

Code writtenby Stefan Ruck on July 25th, 2001.

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

回答by Nick

C++

C++

constexpr hash function:

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
}

回答by Dirk Bester

C++ 11 update of apparently not @MarmouCorp above but http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4067/Switch-on-Strings-in-C.htm

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

Uses two maps to convert between the strings and the class enum (better than plain enum because its values are scoped inside it, and reverse lookup for nice error messages).

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

The use of static in the codeguru code is possible with compiler support for initializer lists which means VS 2013 plus. gcc 4.8.1 was ok with it, not sure how much farther back it would be compatible.

编译器支持初始化列表,这意味着 VS 2013 plus 可以在 codeguru 代码中使用 static 。gcc 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]);
}

回答by tomjen

The problem is that for reasons of optimization the switch statement in C++ does not work on anything but primitive types, and you can only compare them with compile time constants.

问题是,出于优化的原因,C++ 中的 switch 语句除了原始类型外不能用于任何其他类型,您只能将它们与编译时常量进行比较。

Presumably the reason for the restriction is that the compiler is able to apply some form of optimization compiling the code down to one cmp instruction and a goto where the address is computed based on the value of the argument at runtime. Since branching and and loops don't play nicely with modern CPUs, this can be an important optimization.

据推测,限制的原因是编译器能够应用某种形式的优化,将代码编译成一条 cmp 指令和一个 goto,其中地址是根据运行时参数的值计算的。由于分支和循环在现代 CPU 上不能很好地发挥作用,因此这可能是一项重要的优化。

To go around this, I am afraid you will have to resort to if statements.

要解决这个问题,恐怕您将不得不求助于 if 语句。

回答by oklas

Why not? You can use switch implementationwith equivalent syntax and same semantics. The Clanguage does not have objects and strings objects at all, but strings in Cis null terminated strings referenced by pointer. The C++language have possibility to make overload functions for objects comparision or checking objects equalities. As Cas C++is enough flexible to have such switch for strings for Clanguage and for objects of any type that support comparaison or check equality for C++language. And modern C++11allow to have this switch implementation enough effective.

为什么不?您可以使用具有等效语法和相同语义的switch 实现。该C语言根本没有对象和字符串对象,但字符串 inC是指针引用的空终止字符串。该C++语言有可能为对象比较或检查对象相等性制作重载函数。由于C作为C++具有足够的灵活性,以对字符串这样的开关C语言和任何类型的对象支持comparaison或支票平等C++的语言。现代C++11允许这种开关实现足够有效。

Your code will be like this:

您的代码将是这样的:

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"

It is possible to use more complicated types for example std::pairsor any structs or classes that support equality operations (or comarisions for quickmode).

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

Features

特征

  • any type of data which support comparisions or checking equality
  • possibility to build cascading nested switch statemens.
  • possibility to break or fall through case statements
  • possibility to use non constatnt case expressions
  • possible to enable quick static/dynamic mode with tree searching (for C++11)
  • 支持比较或检查相等性的任何类型的数据
  • 构建级联嵌套 switch 语句的可能性。
  • 打破或失败案例陈述的可能性
  • 使用非常量大小写表达式的可能性
  • 可以通过树搜索启用快速静态/动态模式(对于 C++11)

Sintax differences with language switch is

语言切换的语法差异是

  • uppercase keywords
  • need parentheses for CASE statement
  • semicolon ';' at end of statements is not allowed
  • colon ':' at CASE statement is not allowed
  • need one of BREAK or FALL keyword at end of CASE statement
  • 大写关键字
  • CASE 语句需要括号
  • 分号';' 不允许在语句末尾
  • 不允许在 CASE 语句中使用冒号 ':'
  • 在 CASE 语句的末尾需要 BREAK 或 FALL 关键字之一

For C++97language used linear search. For C++11and more modern possible to use quickmode wuth tree search where returnstatement in CASE becoming not allowed. The Clanguage implementation exists where char*type and zero-terminated string comparisions is used.

对于C++97使用线性搜索的语言。对于C++11更现代的可能使用quick模式 wuth 树搜索,其中CASE中的return语句变得不允许。C存在使用char*类型和以零结尾的字符串比较的语言实现。

Read more aboutthis switch implementation.

阅读有关此开关实现的更多信息

回答by rsjaffe

To add a variation using the simplest container possible (no need for an ordered map)... I wouldn't bother with an enum--just put the container definition immediately before the switch so it'll be easy to see which number represents which case.

要使用最简单的容器添加变体(不需要有序映射)......我不会打扰枚举 - 只需将容器定义放在 switch 之前,这样就很容易看到哪个数字代表这种情况下。

This does a hashed lookup in the unordered_mapand uses the associated intto drive the switch statement. Should be quite fast. Note that atis used instead of [], as I've made that container const. Using []can be dangerous--if the string isn't in the map, you'll create a new mapping and may end up with undefined results or a continuously growing map.

这会在 中进行散列查找unordered_map并使用相关联int来驱动 switch 语句。应该是相当快的。请注意,at使用 代替[],因为我已经制作了那个容器const。使用[]可能很危险——如果该字符串不在映射中,您将创建一个新映射,最终可能会得到未定义的结果或不断增长的映射。

Note that the at()function will throw an exception if the string isn't in the map. So you may want to test first using count().

请注意,at()如果字符串不在地图中,该函数将引发异常。因此,您可能想先使用count().

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;


}

The version with a test for an undefined string follows:

对未定义字符串进行测试的版本如下:

const static std::unordered_map<std::string,int> string_to_case{
   {"raj",1},
   {"ben",2}
};
// in C++20, you can replace .count with .contains
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

}

回答by rsjaffe

In C++ and C switches only work on integer types. Use an if else ladder instead. C++ could obviously have implemented some sort of swich statement for strings - I guess nobody thought it worthwhile, and I agree with them.

在 C++ 和 C 中,开关仅适用于整数类型。改用 if else 梯子。C++ 显然可以为字符串实现某种 swich 语句——我想没有人认为这是值得的,我同意他们的看法。

回答by grilix

I think the reason is that in C strings are not primitive types, as tomjen said, think in a string as a char array, so you can not do things like:

我认为原因是在 C 中字符串不是原始类型,正如 tomjen 所说,将字符串视为 char 数组,因此您不能执行以下操作:

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