编译下面的代码会得到错误消息:type illegal。

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

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


当前回答

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

为了生成switch语句的代码,编译器必须理解两个值相等意味着什么。对于int和enum这样的项,这是一个微不足道的比特比较。但是编译器应该如何比较2个字符串值呢?区分大小写,不敏感,文化意识等等……如果没有对弦的充分认识,就不能准确地回答这个问题。

此外,C/ c++ switch语句通常生成为分支表。为字符串样式切换生成分支表远没有那么容易。

其他回答

    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;

    }

在许多情况下,您可以通过从字符串中提取第一个字符并打开它来进行额外的工作。如果您的case以相同的值开始,可能最终必须在charat(1)上进行嵌套切换。任何阅读您的代码的人都会喜欢一个提示,因为大多数人会只使用if-else-if

您可以将字符串放在数组中,并在编译时使用constexpr将它们转换为索引。

constexpr const char* arr[] = { "bar", "foo" };
constexpr int index(const char* str) { /*...*/ }

do_something(std::string str)
{
    switch(quick_index(str))
    {
        case index("bar"):
            // ...
            break;

        case index("foo"):
            // ...
            break;

        case -1:
        default:
            // ...
            break;
    }

对于quick_index,它不一定是constexpr,你可以使用unordered_map在运行时做O(1)。(或者对数组进行排序并使用二进制搜索,参见这里的示例。)

下面是c++ 11的完整示例,使用了一个简单的自定义constexpr字符串比较器。重复的case和不在数组中的case (index给出-1)将在编译时被检测到。遗漏的病例显然没有被发现。后来的c++版本拥有更灵活的constexpr表达式,允许编写更简单的代码。

#include <iostream>
#include <algorithm>
#include <unordered_map>

constexpr const char* arr[] = { "bar", "foo", "foobar" };

constexpr int cmp(const char* str1, const char* str2)
{
    return *str1 == *str2 && (!*str1 || cmp(str1+1, str2+1));
}

constexpr int index(const char* str, int pos=0)
{
    return pos == sizeof(arr)/sizeof(arr[0]) ? -1 : cmp(str, arr[pos]) ? pos : index(str,pos+1);
}

int main()
{
    // initialize hash table once
    std::unordered_map<std::string,int> lookup;
    int i = 0;
    for(auto s : arr) lookup[s] = i++;
    auto quick_index = [&](std::string& s)
        { auto it = lookup.find(s); return it == lookup.end() ? -1 : it->second; };
    
    // usage in code
    std::string str = "bar";
    
    switch(quick_index(str))
    {
        case index("bar"):
            std::cout << "bartender" << std::endl;
            break;

        case index("foo"):
            std::cout << "fighter" << std::endl;
            break;

        case index("foobar"):
            std::cout << "fighter bartender" << std::endl;
            break;
            
        case -1:
        default:
            std::cout << "moo" << std::endl;
            break;
    }
}

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

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

在codeguru代码中使用静态是可能的,因为编译器支持初始化列表,这意味着VS 2013 plus。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]);
}

开关仅适用于整型(int, char, bool等)。为什么不使用映射将字符串与数字配对,然后将该数字与开关使用呢?