Почему оператор switch не может применяться к строкам?
int main() { switch(std::string("raj")) //Compilation error - switch expression of type illegal { case"sda": } }
- Объявление переменных внутри оператора switch
- Есть ли лучшая альтернатива этому, чтобы «включить тип»?
- Почему этот переключатель на корпусе типа считается запутанным?
- Какова относительная разница в производительности if / else по сравнению с оператором switch в Java?
- Случайный охват шкафа
- Почему переключатель быстрее, чем если
- Корпус переключателя типа c #
- Использовать «переход» в коммутаторе?
Причина, почему это связано с системой типов. C / C ++ не поддерживает строки как тип. Он поддерживает идею постоянного массива символов, но на самом деле он не полностью понимает понятие строки.
Чтобы сгенерировать код для оператора switch, компилятор должен понять, что означает, что для двух значений будет равным. Для таких элементов, как int и enums, это тривиальное сравнение бит. Но как компилятор сравнивает 2 строковых значения? Нечувствительны к регистру, нечувствительны, осведомлены о культуре и т. Д. Без полного понимания строки это невозможно точно ответить.
Кроме того, операторы switch C / C ++ обычно генерируются как таблицы ветвей . Не так легко создать таблицу ветвей для переключения стиля строки.
Как упоминалось ранее, компиляторы любят строить таблицы поиска, которые оптимизируют операторы switch
до приближения O (1), когда это возможно. Объедините это с тем, что язык C ++ не имеет строкового типа. Строка std::string
является частью стандартной библиотеки, которая не является частью языка как такового.
Я предложу альтернативу, которую вы, возможно, захотите рассмотреть, я использовал ее в прошлом для хорошего эффекта. Вместо переключения самой строки, переключите результат hash-функции, которая использует строку в качестве входных данных. Ваш код будет почти таким же понятным, как переключение строки, если вы используете предопределенный набор строк:
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
в#include
Код написан Stefan Ruck 25 июля 2001 года.
Проблема заключается в том, что по соображениям оптимизации оператор switch в C ++ не работает ни на чем, кроме примитивных типов, и вы можете сравнивать их только с константами времени компиляции.
Предположительно, причина ограничения заключается в том, что компилятор может применить некоторую форму оптимизации, компилируя код до одной команды cmp и goto, где адрес вычисляется на основе значения аргумента во время выполнения. Поскольку разветвление и петли не очень хорошо сочетаются с современными процессорами, это может быть важной оптимизацией.
Чтобы обойти это, я боюсь, что вам придется прибегать к заявлениям.
Обновление C ++ 11, по-видимому, не @MarmouCorp выше, но http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4067/Switch-on-Strings-in-C.htm
Использует две карты для преобразования между строками и перечислением classа (лучше, чем обычное перечисление, поскольку его значения ограничены внутри него и обратный поиск хороших сообщений об ошибках).
Использование статического кода в кодедегуруре возможно с поддержкой компилятора списков инициализаторов, что означает VS 2013 plus. gcc 4.8.1 было в порядке, не уверен, насколько дальше он будет совместим.
/// /// 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 lambdas pattern без перечислений
unordered_map
для потенциальной амортизации O(1)
: Каков наилучший способ использования HashMap в 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; } }
Вывод:
one 1 two 2 three 3 foobar -1
Использование внутри методов со static
Чтобы эффективно использовать этот шаблон внутри classов, инициализируйте карту lambda статически, иначе вы платите O(n)
каждый раз, чтобы построить ее с нуля.
Здесь мы можем уйти от {}
инициализации переменной static
метода: Статические переменные в методах classа , но мы могли бы также использовать методы, описанные в: static constructors в C ++? Мне нужно инициализировать частные статические объекты
Необходимо было преобразовать захват lambda context [&]
в аргумент, или это было бы undefined: const static auto lambda, используемый с захватом по ссылке
Пример, который производит тот же вывод, что и выше:
#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; } }
В C ++ и C коммутаторы работают только с целыми типами. Вместо этого используйте лестницу if else. Очевидно, что C ++ реализовал какой-то оператор swich для строк – я думаю, никто не думал, что это стоит, и я согласен с ними.
Почему нет? Вы можете использовать реализацию switch с эквивалентным синтаксисом и той же семантикой. Язык C
не имеет объектов и объектов строк вообще, но строки в C
являются строками с нулевым завершением, на которые ссылается указатель. Язык C++
имеет возможность выполнять функции перегрузки для сравнения объектов или проверки соответствия объектов. Поскольку C
как C++
достаточно гибкий, чтобы иметь такой переключатель для строк для языка C
и для объектов любого типа, которые поддерживают comparaison или проверяют равенство для языка 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
или любые структуры или classы, которые поддерживают операции равенства (или comarisions для быстрого режима).
Особенности
- любые типы данных, которые поддерживают сравнения или проверяют равенство
- возможность создания каскадных вложенных коммутаторов statemens.
- возможность взломать или оспаривать заявления о случаях
- возможность использования нестандартных выражений case
- можно включить быстрый статический / динамический режим с поиском дерева (для C ++ 11)
Различия в Sintax с языковым переключателем
- заглавные слова
- нужны скобки для выражения CASE
- точка с запятой ‘;’ в конце заявлений не допускается
- colon ‘:’ в CASE-заявлении не допускается
- требуется одно из BREAK или FALL ключевого слова в конце выражения CASE
Для C++97
используется линейный поиск. Для C++11
и более современных можно использовать поиск в режиме быстрого поиска wuth, где оператор возврата в CASE становится недопустимым. Реализация языка C
существует там, где используется тип char*
и нулевые концевые сравнения строк.
Подробнее об этой реализации коммутатора.
В C ++ вы можете использовать оператор switch только для int и char
Я думаю, что причина в том, что в строках C нет примитивных типов, как сказал tomjen, думайте в строке как массив символов, поэтому вы не можете делать такие вещи, как:
switch (char[]) { // ... switch (int[]) { // ...
C ++
constexpr hash-функция:
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 ++ строки не являются первоclassными гражданами. Строковые операции выполняются через стандартную библиотеку. Думаю, вот в чем причина. Кроме того, C ++ использует оптимизацию таблицы ветвей для оптимизации операторов case switch. Посмотрите на ссылку.
Чтобы добавить вариацию с использованием самого простого контейнера (нет необходимости в упорядоченной карте) … Я бы не стал беспокоиться о перечислении – просто поставьте определение контейнера непосредственно перед коммутатором, чтобы было легко увидеть, какое число представляет в этом случае.
Это делает хешированный поиск в unordered_map
и использует связанный int
для управления оператором switch. Должно быть довольно быстро. Обратите внимание, что at
используется вместо []
, поскольку я создал содержащую const
. Использование []
может быть опасным – если строка отсутствует на карте, вы создадите новое сопоставление и можете в конечном итоге получить неопределенные результаты или постоянно растущую карту.
Обратите внимание, что функция at()
генерирует исключение, если строка не находится на карте. Таким образом, вы можете сначала протестировать 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; }
Версия с тестом на неопределенную строку следует:
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 }
Вы не можете использовать строку в case switch. Только int & char разрешен. Вместо этого вы можете попробовать enum для представления строки и использовать ее в блоке case switch, например
enum MyString(raj,taj,aaj);
Используйте его в инструкции 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), если ваши случаи начинаются с того же значения. любой, кто читает ваш код, по достоинству оценит намек, потому что большинство будет пытаться просто if-else-if
Это потому, что 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 ...
(код из wikipedia https://en.wikipedia.org/wiki/Branch_table )