Por que a instrução switch não pode ser aplicada em strings?

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

A razão por que tem a ver com o sistema de tipos. C / C ++ não suporta realmente strings como um tipo. Ele suporta a ideia de uma matriz char constante, mas não entende totalmente a noção de uma string.

Para gerar o código para uma instrução switch, o compilador deve entender o que significa que dois valores sejam iguais. Para itens como ints e enums, essa é uma comparação de bits trivial. Mas como o compilador deve comparar dois valores de string? Caso sensível, insensível, cultura consciente, etc … Sem uma plena consciência de uma corda isso não pode ser respondido com precisão.

Além disso, as instruções de opção C / C ++ geralmente são geradas como tabelas de ramificação . Não é tão fácil gerar uma tabela de ramificação para um comutador de estilo de cadeia de caracteres.

Como mencionado anteriormente, os compiladores gostam de construir tabelas de pesquisa que otimizam as instruções de switch para o tempo O (1) próximo sempre que possível. Combine isso com o fato de que a Linguagem C ++ não possui um tipo de cadeia de caracteres – std::string faz parte da Biblioteca Padrão que não faz parte do Idioma em si.

Eu vou oferecer uma alternativa que você pode querer considerar, eu usei no passado com bons resultados. Em vez de alternar a própria cadeia de caracteres, alterne o resultado de uma function hash que usa a cadeia como input. Seu código será quase tão claro quanto mudar a string se você estiver usando um conjunto predeterminado de 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: ... } } 

Há um monte de otimizações óbvias que praticamente seguem o que o compilador C faria com uma instrução switch … engraçado como isso acontece.

Você só pode usar opções primitivas como int, char e enum. A solução mais fácil de fazer como você deseja é usar um enum.

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

Código escrito por Stefan Ruck em 25 de julho de 2001.

O problema é que, por razões de otimização, a instrução switch em C ++ não funciona em nada além de tipos primitivos, e você só pode compará-los com constantes de tempo de compilation.

Presumivelmente, a razão para a restrição é que o compilador é capaz de aplicar alguma forma de otimização compilando o código para uma instrução cmp e um goto onde o endereço é calculado com base no valor do argumento em tempo de execução. Como ramificações e loops não funcionam bem com CPUs modernas, essa pode ser uma otimização importante.

Para contornar isso, receio que você tenha que recorrer a declarações if.

C + + 11 atualização aparentemente não @MarmouCorp acima, mas http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4067/Switch-on-Strings-in-C.htm

Usa dois mapas para converter entre as strings e o enum de class (melhor que enum simples porque seus valores são escopos dentro dele e pesquisa inversa para mensagens de erro agradáveis).

O uso de estática no código codeguru é possível com o suporte do compilador para listas de boot, o que significa VS 2013 plus. gcc 4.8.1 foi ok com ele, não tenho certeza quanto mais atrás seria compatível.

 ///  /// Enum for String values we want to switch on ///  enum class TestType { SetType, GetType }; ///  /// Map from strings to enum values ///  std::map MnCTest::s_mapStringToTestType = { { "setType", TestType::SetType }, { "getType", TestType::GetType } }; ///  /// Map from enum values to strings ///  std::map 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]); } 

std::map + C + + 11 padrão lambdas sem enums

unordered_map para o potencial amortizado O(1) : Qual é a melhor maneira de usar um HashMap em C ++?

 #include  #include  #include  #include  #include  int main() { int result; const std::unordered_map> m{ {"one", [&](){ result = 1; }}, {"two", [&](){ result = 2; }}, {"three", [&](){ result = 3; }}, }; const auto end = m.end(); std::vector 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; } } 

Saída:

 one 1 two 2 three 3 foobar -1 

Uso dentro de methods com static

Para usar esse padrão de forma eficiente dentro de classs, inicialize o mapa lambda estaticamente, ou então pague O(n) toda vez para construí-lo do zero.

Aqui podemos nos livrar da {} boot de uma variável de método static : variables estáticas em methods de class , mas também podemos usar os methods descritos em: construtores estáticos em C ++? Eu preciso inicializar objects estáticos privados

Foi necessário transformar a captura de contexto lambda [&] em um argumento, ou que teria sido indefinido: auto lambda const estático usado com captura por referência

Exemplo que produz a mesma saída acima:

 #include  #include  #include  #include  #include  class RangeSwitch { public: void method(std::string key, int &result) { static const std::unordered_map> 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 strings{"one", "two", "three", "foobar"}; for (const auto& s : strings) { rangeSwitch.method(s, result); std::cout < < s << " " << result << std::endl; } } 

Em C ++ e C, os switches funcionam apenas em tipos inteiros. Use uma escada if else em vez disso. C ++ poderia obviamente ter implementado algum tipo de instrução swich para strings – acho que ninguém achou valer a pena, e eu concordo com eles.

Por que não? Você pode usar a implementação de switch com syntax equivalente e a mesma semântica. A linguagem C não possui objects objects e strings, mas strings em C são strings terminadas em null referenciadas por pointer. A linguagem C++ tem a possibilidade de fazer funções de sobrecarga para comparação de objects ou verificação de igualdades de objects. Como C como C++ é bastante flexível para ter essa opção para seqüências de caracteres para linguagem C e para objects de qualquer tipo que suportam comparações ou verificar a igualdade para a linguagem C++ . E o moderno C++11 permite que a implementação desse switch seja eficiente.

Seu código será assim:

 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" 

É possível usar tipos mais complicados, por exemplo, std::pairs ou quaisquer estruturas ou classs que suportem operações de igualdade (ou comarisions para o modo rápido ).

Características

  • qualquer tipo de dado que suporte comparações ou verificação de igualdade
  • possibilidade de construir statemens de switch nesteds em cascata.
  • possibilidade de quebrar ou cair através de declarações de casos
  • possibilidade de usar expressões de casos não constantes
  • possível ativar o modo estático / dynamic rápido com pesquisa de tree (para C ++ 11)

As diferenças de syntax com a troca de idioma são

  • palavras-chave maiúsculas
  • precisa de parênteses para a instrução CASE
  • ponto e vírgula ‘;’ no final das instruções não é permitido
  • cólon ‘:’ na instrução CASE não é permitido
  • precisa de uma palavra-chave BREAK ou FALL no final da instrução CASE

Para linguagem C++97 usou busca linear. Para C++11 e mais moderno possível usar quick modo quick wuth tree search onde a declaração de retorno no CASE não é permitida. A implementação da linguagem C existe onde o tipo char* e as comparações de strings terminadas em zero são usadas.

Leia mais sobre essa implementação de switch.

Em C ++ você só pode usar uma instrução switch no int e char

Eu acho que a razão é que em C strings não são tipos primitivos, como disse Tomjen, pense em uma string como uma matriz char, então você não pode fazer coisas como:

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

C ++

function hash 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 } 

Em c ++ strings não são cidadãos de primeira class. As operações de string são feitas através da biblioteca padrão. Eu acho que essa é a razão. Além disso, o C ++ usa a otimização da tabela de ramificação para otimizar as instruções de caso de opção. Dê uma olhada no link.

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

Para adicionar uma variação usando o container mais simples possível (sem necessidade de um mapa ordenado) … Eu não me incomodaria com um enum – basta colocar a definição do container imediatamente antes do switch, então será fácil ver qual número representa qual caso.

Isso faz uma pesquisa com hash no unordered_map e usa o int associado para conduzir a instrução switch. Deve ser bem rápido. Note que at é usado em vez de [] , como eu fiz que continha const . Usar [] pode ser perigoso – se a string não estiver no mapa, você criará um novo mapeamento e poderá acabar com resultados indefinidos ou com um mapa em constante crescimento.

Note que a function at() lançará uma exceção se a string não estiver no mapa. Então você pode querer testar primeiro usando count() .

 const static std::unordered_map 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; } 

A versão com um teste para uma sequência indefinida segue:

 const static std::unordered_map string_to_case{ {"raj",1}, {"ben",2} }; 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 } 

Você não pode usar string no switch case.Only int & char são permitidos. Em vez disso, você pode tentar o enum para representar a string e usá-la no bloco de casos de switch como

 enum MyString(raj,taj,aaj); 

Use-o na declaração do caso swich.

Os comutadores funcionam apenas com tipos integrais (int, char, bool, etc.). Por que não usar um mapa para emparelhar uma string com um número e depois usar esse número com o 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; } 

em muitos casos, você pode obter um trabalho extra puxando o primeiro caractere da string e ligando-o. pode acabar tendo que fazer um switch nested no charat (1) se seus casos começarem com o mesmo valor. qualquer um que lê seu código apreciaria uma sugestão embora porque a maioria prob apenas se-se-se

Isso porque o C ++ transforma os switches em tabelas de salto. Ele executa uma operação trivial nos dados de input e pula para o endereço correto sem comparar. Como uma string não é um número, mas uma matriz de números, o C ++ não pode criar uma tabela de salto a partir dela.

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

(código da wikipedia https://en.wikipedia.org/wiki/Branch_table )