284

次のコードをコンパイルすると、 のエラーが発生しましたtype illegal

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

switchまたはで文字列を使用することはできませんcase。なんで?文字列をオンにするのと同様のロジックをサポートするためにうまく機能するソリューションはありますか?

4

22 に答える 22

229

その理由は、型システムに関係しています。C/C++ は実際には文字列を型としてサポートしていません。定数 char 配列の考え方はサポートしていますが、文字列の概念を完全には理解していません。

switch ステートメントのコードを生成するために、コンパイラは 2 つの値が等しいという意味を理解する必要があります。int や enum などの項目の場合、これは些細な比較です。しかし、コンパイラは 2 つの文字列値をどのように比較すればよいのでしょうか? 大文字と小文字を区別する、区別しない、文化を意識するなど...文字列を完全に認識していないと、これに正確に答えることができません。

さらに、C/C++ の switch ステートメントは通常、ブランチ テーブルとして生成されます。文字列スタイルのスイッチのブランチ テーブルを生成するのはそれほど簡単ではありません。

于 2009-03-16T12:30:36.803 に答える
67

前述のように、コンパイラは、switchステートメントを可能な限り O(1) タイミング近くに最適化するルックアップ テーブルを作成することを好みます。これを、C++ 言語には文字列型がないという事実と組み合わせます。これstd::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ステートメントで行うこととほぼ同じように、明らかな最適化がたくさんあります...それがどのように起こるかは面白いです。

于 2009-03-16T12:52:08.530 に答える
19

上記の @MarmouCorp ではなく、明らかにhttp://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4067/Switch-on-Strings-in-C.htmの C++ 11 更新

2 つのマップを使用して、文字列とクラス列挙型の間で変換します (その値はその内部でスコープされているため、単純な列挙型よりも優れており、適切なエラー メッセージの逆ルックアップが行われます)。

codeguru コードでの static の使用は、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]);
}
于 2014-12-10T00:07:27.413 に答える
13

問題は、最適化の理由から、C++ の switch ステートメントはプリミティブ型以外には機能せず、それらをコンパイル時の定数としか比較できないことです。

おそらく、この制限の理由は、コードを 1 つの cmp 命令と、実行時に引数の値に基づいてアドレスが計算される goto にコンパイルする何らかの形式の最適化をコンパイラが適用できるためです。分岐とループは最新の CPU ではうまく機能しないため、これは重要な最適化になる可能性があります。

これを回避するには、if ステートメントに頼る必要があると思います。

于 2009-03-16T12:24:53.667 に答える
6

C++ および C では、スイッチは整数型でのみ機能します。代わりに if else はしごを使用してください。C++ は明らかに、文字列に対してある種のswich ステートメントを実装できたはずです。誰もそれを価値があるとは考えていなかったと思いますが、私は彼らに同意します。

于 2009-03-16T12:17:44.730 に答える
4

その理由は、Tomjenが言ったように、C文字列はプリミティブ型ではないため、文字列をchar配列と考えているため、次のようなことはできないと思います。

switch (char[]) { // ...
switch (int[]) { // ...
于 2009-03-16T12:31:01.437 に答える
3

C++ では、文字列は第一級市民ではありません。文字列操作は、標準ライブラリを介して行われます。それが理由だと思います。また、C++ は分岐テーブルの最適化を使用して switch case ステートメントを最適化します。リンクを見てください。

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

于 2009-03-16T13:28:29.693 に答える
2

C++ では、int と char に対してのみ switch ステートメントを使用できます。

于 2009-03-16T12:15:25.920 に答える
0
    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;

    }
于 2016-03-08T16:58:26.920 に答える
0

ニックのソリューションに対するウサギのコメントは本当にクールです。完全なコード例 (C++11):

constexpr uint32_t hash(const std::string& s) noexcept
{
    uint32_t hash = 5381;
    for (const auto& c : s)
        hash = ((hash << 5) + hash) + (unsigned char)c;
    return hash;
}

constexpr inline uint32_t operator"" _(char const* p, size_t) { return hash(p); }

std::string s = "raj";
switch (hash(s)) {
case "sda"_:
    // do_something();
    break;
default:
    break;
}
于 2021-10-26T09:37:30.823 に答える
-1

スイッチは整数型 (int、char、bool など) でのみ機能します。マップを使用して文字列と数値をペアにし、その数値をスイッチで使用してみませんか?

于 2012-08-11T04:21:19.680 に答える
-1

switch case では string を使用できません。int と char のみが許可されます。代わりに、文字列を表すために列挙型を試して、次のように switch case ブロックで使用できます

enum MyString(raj,taj,aaj);

switch case ステートメントで使用します。

于 2009-03-16T12:28:34.593 に答える
-1

多くの場合、文字列から最初の文字を取り出してオンにすることで、余分な作業を行うことができます。ケースが同じ値で始まる場合、 charat(1) でネストされたスイッチを実行する必要がある場合があります。あなたのコードを読んでいる人は誰でもヒントをいただければ幸いです。

于 2016-09-23T03:11:15.703 に答える
-2

これは、C++ がスイッチをジャンプ テーブルに変換するためです。入力データに対して簡単な操作を実行し、比較せずに適切なアドレスにジャンプします。文字列は数値ではなく数値の配列であるため、C++ はそれからジャンプ テーブルを作成できません。

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 )

于 2016-03-24T19:46:31.043 に答える