Why the switch statement cannot be applied on strings?

前端 未结 20 2590
离开以前
离开以前 2020-11-22 03:58

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

int main()
{
    // Compilation error - switch expression of type illegal
    sw         


        
相关标签:
20条回答
  • 2020-11-22 04:28

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

    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::pairs or any structs or classes that support equality operations (or comarisions for quick mode).

    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)

    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

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

    Read more about this switch implementation.

    0 讨论(0)
  • 2020-11-22 04:30

    std::map + C++11 lambdas pattern without enums

    unordered_map for the potential amortized O(1): What is the best way to use a HashMap in C++?

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

    Output:

    one 1
    two 2
    three 3
    foobar -1
    

    Usage inside methods with static

    To use this pattern efficiently inside classes, initialize the lambda map statically, or else you pay O(n) every time to build it from scratch.

    Here we can get away with the {} initialization of a static method variable: Static variables in class methods , but we could also use the methods described at: static constructors in C++? I need to initialize private static objects

    It was necessary to transform the lambda context capture [&] into an argument, or that would have been undefined: const static auto lambda used with capture by reference

    Example that produces the same output as above:

    #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;
        }
    }
    
    0 讨论(0)
  • 2020-11-22 04:30

    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.

    0 讨论(0)
  • 2020-11-22 04:31

    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.

    This does a hashed lookup in the unordered_map and uses the associated int to drive the switch statement. Should be quite fast. Note that at is 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.

    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().

    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
    
    }
    
    0 讨论(0)
  • 2020-11-22 04:31

    In c++ strings are not first class citizens. The string operations are done through standard library. I think, that is the reason. Also, C++ uses branch table optimization to optimize the switch case statements. Have a look at the link.

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

    0 讨论(0)
  • 2020-11-22 04:32

    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:

    switch (char[]) { // ...
    switch (int[]) { // ...
    
    0 讨论(0)
提交回复
热议问题