Почему оператор switch не может быть применен к строкам?
компиляция следующего кода и получил ошибку type illegal.
int main()
{
// Compilation error - switch expression of type illegal
switch(std::string("raj"))
{
case"sda":
}
}
вы не можете использовать строку в либо switch или case. Зачем? Есть ли какое-либо решение, которое хорошо работает для поддержки логики, подобной включению строк?
18 ответов:
причина, почему это связано с системой типов. C / C++ на самом деле не поддерживает строки как тип. Он поддерживает идею постоянного массива символов, но на самом деле не полностью понимает понятие строки.
чтобы сгенерировать код для оператора switch компилятор должен понимать, что значит для двух значений быть равным. Для таких элементов, как ints и enums, это тривиальное сравнение битов. Но как компилятор должен сравнивать 2 строковых значения? Случай чувствительный, нечувствительный, осведомленный о культуре и т. д... Без полного осознания струны на это нельзя точно ответить.
кроме того, операторы коммутатора C/C++ обычно генерируются как таблицы. Не так просто создать таблицу ветвей для переключателя стиля строки.
как упоминалось ранее, компиляторы любят создавать таблицы поиска, которые оптимизируют
switchоператоры, чтобы приблизиться к O (1) времени, когда это возможно. Объедините это с тем, что язык C++ не имеет строкового типа -std::stringявляется частью стандартной библиотеки, которая не является частью языка как такового.я предложу альтернативу, которую вы, возможно, захотите рассмотреть, я использовал ее в прошлом для хорошего эффекта. Вместо переключения самой строки переключите результат a хэш-функция, которая использует строку в качестве входных данных. Ваш код будет почти так же ясно, как переключение строки, Если вы используете заранее определенный набор строк:
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... забавно, как это происходит.
вы можете использовать только примитив включения, такой как int, char и enum. Самое простое решение сделать это так, как вы хотите, это использовать перечисление.
#include <map> #include <string> #include <iostream.h> // 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<std::string, StringValue> 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; }код, написанный Стефан рук 25 июля 2001 года.
проблема в том, что по причинам оптимизации оператор switch в C++ не работает ни на чем, кроме примитивных типов, и вы можете сравнивать их только с константами времени компиляции.
Предположительно причина ограничения заключается в том, что компилятор может применять некоторую форму оптимизации, компилируя код до одной инструкции cmp и goto, где адрес вычисляется на основе значения аргумента во время выполнения. Так как ветвление и и петли не играют хорошо с современными процессорами это может быть важной оптимизацией.
чтобы обойти это, я боюсь, вам придется прибегнуть к утверждениям if.
C++ 11 обновление, по-видимому, не @MarmouCorp выше, но http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4067/Switch-on-Strings-in-C.htm
использует две карты для преобразования между строками и перечислением класса (лучше, чем простое перечисление, потому что его значения ограничены внутри него, и обратный поиск для хороших сообщений об ошибках).
использование статики в коде codeguru возможно при поддержке компилятора для списков инициализаторов, что означает VS 2013 plus. ССЗ 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]); }
std::map+ C++11 lambdas pattern без перечислений
unordered_mapдля потенциальной амортизацииO(1):что является лучшим способом, чтобы использовать HashMap в языке 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; } }выход:
one 1 two 2 three 3 foobar -1использование внутри методов с
staticчтобы эффективно использовать этот шаблон внутри классов, статически инициализируйте лямбда-карту, иначе вы платите
O(n)каждый раз, чтобы построить его из царапать.здесь мы можем уйти с
{}инициализация astaticпеременной метод: статические переменные в методах класса, но мы могли бы также использовать методы, описанные в: статические конструкторы в 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 переключатели работают только на целочисленных типах. Вместо этого используйте лестницу if else. C++, очевидно, мог бы реализовать какой - то оператор swich для строк-я думаю, никто не думал, что это стоит, и я согласен с ними.
почему бы и нет? Вы можете использовать переключатель реализации с эквивалентным синтаксисом и той же семантикой. Элемент
Cязык не имеет объектов и объектов строк, но строки вC- Это строки с нулевым завершением, на которые ссылается указатель. ЭлементC++язык имеет возможность сделать функции перегрузки для сравнение объектов или проверка равенства объектов. КакCкакC++достаточно гибким, чтобы иметь такой переключатель для строкиCязык и для объектов любого типа, которые поддерживают сравнение или проверку равенство дляC++язык. И современныйC++11разрешить этот переключатель реализация достаточно эффективна.ваш код будет такой:
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"можно использовать более сложные типы, например
std::pairsили любые структуры или классы, которые поддерживают операции равенства (или comarisions для быстрая режим).особенности
- любой тип данных, которые поддерживают сравнения или проверка равенства
- возможность построения каскадных вложенных состояний коммутатора.
- возможность сломать или провалиться через case заявления
- возможность использовать не константные выражения case
- можно включить быстрый статический / динамический режим с поиском дерева (для C++11)
различия Sintax с переключателем языка
- верхний регистр ключевых слов
- нужны скобки для CASE statement
- точка с запятой ';' в конце заявления не допускается
- двоеточие': 'в случае заявление не допускается
- нужно одно из ключевых слов BREAK или FALL в конце оператора CASE
на
C++97язык используется линейный поиск. ИбоC++11и более современный можно использоватьquickрежим wuth дерева поиска, где возвращение заявление в случае становления не допускается. ЭлементCреализация языка существует там, гдеchar*используется сравнение типа и нулевой строки.читать подробнее о реализация данного переключателя.
Я думаю, причина в том, что в строках C не являются примитивными типами, как сказал томьен, думайте в строке как массив символов, поэтому вы не можете делать такие вещи, как:
switch (char[]) { // ... switch (int[]) { // ...
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 }
В C++ строки не первого сорта. Строковые операции выполняются через стандартную библиотеку. Я думаю, что это причина. Кроме того, C++ использует оптимизацию таблицы ветвей для оптимизации операторов switch case. Взгляните на ссылку.
чтобы добавить вариацию, используя самый простой контейнер (нет необходимости в упорядоченной карте)... Я бы не стал беспокоиться о перечислении-просто поместите определение контейнера непосредственно перед переключателем, чтобы было легко увидеть, какое число представляет какой случай.
это делает хэшированный поиск в
unordered_mapи использует соответствующуюintдля управления оператором switch. Должно быть довольно быстро. Обратите внимание, чтоatвместо[], как я сделал, что содержащаясяconst. С помощью[]может быть опасно--если строка не находится на карте, вы создадите новое отображение и можете получить неопределенные результаты или постоянно растущую карту.отметим, что
at()функция выдаст исключение, если строка не находится на карте. Поэтому вы можете сначала протестировать с помощью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; }версия с тестом для неопределенной строки выглядит следующим образом:
const static std::unordered_map<std::string,int> 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 }
вы не можете использовать строку в случае переключатель.Разрешены только int & char. Вместо этого вы можете попробовать перечисление для представления строки и использовать его в блоке switch case, например
enum MyString(raj,taj,aaj);используйте его int оператор Swich case.
коммутаторы работают только с интегральными типами (int, char, bool и т. д.). Почему бы не использовать карту для сопряжения строки с числом, а затем использовать это число с переключателем?
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; }
во многих случаях вы можете заядлый дополнительную работу, потянув первый символ из строки и включения этого. может закончиться тем, что вам придется сделать вложенный переключатель на charat(1), если ваши случаи начинаются с того же значения. любой, кто читает ваш код, будет признателен за подсказку, хотя, потому что большинство будет пробовать только если-иначе-если
это потому, что 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)
Comments