switch 문을 문자열에 적용 할 수없는 이유는 무엇입니까? {

다음 코드를 컴파일하고의 오류가 발생했습니다 type illegal.

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

switch또는 에서 문자열을 사용할 수 없습니다 case. 왜? 문자열 켜기와 유사한 논리를 지원하기 위해 잘 작동하는 솔루션이 있습니까?



답변

타입 시스템과 관련이있는 이유. C / C ++는 실제로 문자열을 유형으로 지원하지 않습니다. 상수 char 배열의 아이디어를 지원하지만 실제로 문자열의 개념을 완전히 이해하지는 못합니다.

switch 문에 대한 코드를 생성하려면 컴파일러는 두 값이 동일하다는 의미를 이해해야합니다. 정수 및 열거 형과 같은 항목의 경우 이는 간단한 비트 비교입니다. 그러나 컴파일러는 2 개의 문자열 값을 어떻게 비교해야합니까? 대소 문자 구분, 둔감, 문화 인식 등 … 문자열을 완전히 인식하지 못하면 정확하게 대답 할 수 없습니다.

또한 C / C ++ 스위치 문은 일반적으로 분기 테이블 로 생성됩니다 . 문자열 스타일 스위치에 대한 분기 테이블을 생성하는 것은 쉽지 않습니다.


답변

앞에서 언급했듯이 컴파일러는 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 문을 사용하여 수행하는 작업을 거의 따르는 많은 명백한 최적화가 있습니다.


답변

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
}

답변

위의 @MarmouCorp가 아닌 http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4067/Switch-on-Strings-in-C.htm 의 C ++ 11 업데이트

두 개의 맵을 사용하여 문자열과 클래스 열거 형 사이를 변환합니다 (값이 범위 내에 있기 때문에 일반 열거 형보다 낫고 오류 메시지가 좋은 경우 역방향 조회).

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]);
}

답변

문제는 최적화의 이유로 C ++의 switch 문이 기본 유형 이외의 다른 유형에서는 작동하지 않으며 컴파일 시간 상수와 만 비교할 수 있다는 것입니다.

아마도 제한 이유는 컴파일러가 코드를 컴파일하는 일부 최적화 형식을 하나의 cmp 명령어와 런타임시 인수 값을 기반으로 주소가 계산되는 goto로 적용 할 수 있기 때문입니다. 분기 및 루프는 최신 CPU에서 잘 작동하지 않기 때문에 중요한 최적화가 될 수 있습니다.

이 문제를 해결하려면 if 문에 의지해야합니다.


답변

std::map 열거 형이없는 +++++ 람다 패턴

unordered_map할부 상환 가능성 O(1): C ++에서 HashMap을 사용하는 가장 좋은 방법은 무엇입니까?

#include <functional>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>

int main() {
    int result;
    const std::unordered_map<std::string,std::function<void()>> m{
        {"one",   [&](){ result = 1; }},
        {"two",   [&](){ result = 2; }},
        {"three", [&](){ result = 3; }},
    };
    const auto end = m.end();
    std::vector<std::string> strings{"one", "two", "three", "foobar"};
    for (const auto& s : strings) {
        auto it = m.find(s);
        if (it != end) {
            it->second();
        } else {
            result = -1;
        }
        std::cout << s << " " << result << std::endl;
    }
}

산출:

one 1
two 2
three 3
foobar -1

내부 메소드 사용 static

클래스 내에서이 패턴을 효율적으로 사용하려면 람다 맵을 정적으로 초기화하십시오. 그렇지 않으면 O(n)처음부터 빌드 할 때마다 지불 합니다.

여기 {}에서 static메소드 변수 의 초기화를 피할 수 있습니다 : 클래스 methods의 정적 변수 . 그러나 C ++의 정적 생성자에 설명 된 메소드를 사용할 수도 있습니다 . 개인 정적 객체를 초기화해야합니다

람다 컨텍스트 캡처 [&]를 인수로 변환해야 하거나 정의되지 않았을 수 있습니다. const 정적 자동 람다 참조로 캡처와 함께 사용

위와 동일한 출력을 생성하는 예 :

#include <functional>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>

class RangeSwitch {
public:
    void method(std::string key, int &result) {
        static const std::unordered_map<std::string,std::function<void(int&)>> m{
            {"one",   [](int& result){ result = 1; }},
            {"two",   [](int& result){ result = 2; }},
            {"three", [](int& result){ result = 3; }},
        };
        static const auto end = m.end();
        auto it = m.find(key);
        if (it != end) {
            it->second(result);
        } else {
            result = -1;
        }
    }
};

int main() {
    RangeSwitch rangeSwitch;
    int result;
    std::vector<std::string> strings{"one", "two", "three", "foobar"};
    for (const auto& s : strings) {
        rangeSwitch.method(s, result);
        std::cout << s << " " << result << std::endl;
    }
}

답변

C ++ 및 C 스위치에서는 정수 유형에서만 작동합니다. 대신 else 사다리를 사용하십시오. C ++은 분명히 문자열에 대해 일종의 swich 문을 구현했을 수 있습니다. 아무도 가치가 없다고 생각하고 동의합니다.