Как выбрать диапазон значений в инструкции switch?

Когда я пытаюсь скомпилировать, я получаю эту ошибку:

1>------ Build started: Project: snake, Configuration: Debug Win32 ------
1>  exercise.cpp
1>c:\users\robin\documents\visual studio 2010\projects\snake\snake\exercise.cpp(13): error C2059: syntax error : '>='
1>c:\users\robin\documents\visual studio 2010\projects\snake\snake\exercise.cpp(16): error C2059: syntax error : '>='
1>c:\users\robin\documents\visual studio 2010\projects\snake\snake\exercise.cpp(19): error C2059: syntax error : '>='
1>c:\users\robin\documents\visual studio 2010\projects\snake\snake\exercise.cpp(22): error C2059: syntax error : '>='
1>c:\users\robin\documents\visual studio 2010\projects\snake\snake\exercise.cpp(25): error C2059: syntax error : '>'
1>c:\users\robin\documents\visual studio 2010\projects\snake\snake\exercise.cpp(28): error C2059: syntax error : '=='
1>c:\users\robin\documents\visual studio 2010\projects\snake\snake\exercise.cpp(34): warning C4065: switch statement contains 'default' but no 'case' labels
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

код:

#include <iostream>
using namespace std;

int main(){
    int score;

    //Vraag de score
    cout << "Score:";
    cin >> score;

    //Switch
    switch(score){
        case >= 100:
            cout << "a";
            break;
        case >= 50:
            cout << "b";
            break;
        case >= 25:
            cout << "c";
            break;
        case >= 10:
            cout << "d";
            break;
        case > 0:
            cout << "e";
            break;
        case == 0:
            cout << "f";
            break;
        default:
            cout << "BAD VALUE";
            break;
    }
    cout << endl;
    return 0;
}

Как я могу исправить эту проблему? Это консольное приложение, Win32 и моя IDE - это Windows Enterprise С++ 2010.

Я изучаю начало программирования на С++ через игровое программирование.

Ответ 1

В С++ case метки являются постоянными выражениями, а не выражениями вообще. Вам нужна цепочка операторов if-then-else, чтобы делать то, что вы пытаетесь сделать.

В качестве альтернативы вы можете перечислить значения в коммутаторе. Это выполняется немного быстрее (хотя это не имеет значения в таких случаях, как у вас), но это значительно менее читаемо:

switch(score) {
    case 0: cout << "f"; break;
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
    case 8:
    case 9:
    case 10: cout << "e"; break;
    case 11:
    case 12:
    case 13:
    case 14:
    case 15:
    case 16:
    case 17:
    case 18:
    case 19:
    case 20:
    case 21:
    case 22:
    case 23:
    case 24:
    case 25: cout << "c"; break;
    // ...and so on, you get the idea...

}

Ответ 2

Некоторые компиляторы поддерживают диапазоны регистров типа case x ... y как расширение на языке С++.

Пример:

#include <iostream>
using namespace std;

int main(){
    int score;

    //Vraag de score
    cout << "Score:";
    cin >> score;

    //Switch
    switch(score){
       case 0:
            cout << "a";
            break;
       case 0 ... 9:
            cout << "b";
            break;
       case 11 ... 24:
            cout << "c";
            break;
       case 25 ... 49:
            cout << "d";
            break;
       case 50 ... 100:
            cout << "e";
            break;         
        default:
            cout << "BAD VALUE";
            break;
    }
    cout << endl;
    return 0;
}

GCC 4.9, Clang 3.5.1 и компилятор Intel C/С++ 13.0.1, похоже, поддерживают его (проверено на http://gcc.godbolt.org/). С другой стороны, Visual С++ 19 не (проверено на http://webcompiler.cloudapp.net/).

Ответ 3

Вы можете исправить эту проблему, используя серию операторов if/else if. Переключатель/регистр нельзя использовать как на С++.

Ответ 4

Это можно сделать, используя std::map с switch:

enum Interval {
   One,
   Two,
   Three,
   NotFound };

// [0,10[ is One, [10,30[ is Two, [30,55[ is Three
std::map<int,Interval> imap { 
    { { 0, One }, 
      { 10, Two },
      { 30, Three },
      { 55, NotFound } };
Interval ivalue = NotFound;
auto f = imap.lower_bound( value );
if( f != imap.end() ) ivalue = f->second;
switch( ivalue ) {
    case One : ...
    case Two : ...
    case Three : ...
    default: ...
}

Ответ 5

В С++ оператор switch может соответствовать только целым целочисленным значениям:

switch (i)
{
    case 1:
    //... stuff
    break;
    case 2:
    //... stuff
    break;
    default:
    //... stuff
}

Ответ 6

Там расширение GCC, которое делает именно то, что вы хотите.

Ответ 7

Стандарт не позволяет этого:

6.4.2 Оператор switch [stmt.switch]

[...] Любой оператор в инструкции switch может быть помечен одним или несколькими ярлыками case следующим образом:

case constant-expression :

где константное выражение должно быть интегральным постоянным выражением (5.19).

Другими словами, вы можете использовать только значения case, которые расширяются в единую интегральную постоянную постоянную времени компиляции (например, case 5+6:, enum {X = 3}; ... case X*X:).

Способы использования if -statements. Например, чтобы заменить

switch (x)
case 0..100:

вместо этого вы

if (x>=0 && x<=100)

.

Ответ 8

std::map::upper_bound + С++ 11 lambdas

fooobar.com/questions/79046/... упоминается lower_bound, но мы также можем избавиться от enum там с lambdas (или наследованием, если у вас его нет).

#include <functional>
#include <iostream>
#include <map>

int main() {
    std::string ret;
    const std::map<int,std::function<void()>> m{
        {0, [&](){ ret = "too small"; }},
        {2, [&](){ ret = "[0,2)"; }},
        {5, [&](){ ret = "[2,5)"; }},
        {7, [&](){ ret = "[5,7)"; }},
    };
    const auto end = m.end();
    for (auto i = -1; i < 8; ++i) {
        auto it = m.upper_bound(i);
        if (it == end) {
            ret = "too large";
        } else {
            it->second();
        }
        std::cout << i << " " << ret << std::endl;
    }
}

Вывод:

-1 too small
0 [0,2)
1 [0,2)
2 [2,5)
3 [2,5)
4 [2,5)
5 [5,7)
6 [5,7)
7 too large

Использование внутри методов с помощью static

Чтобы эффективно использовать этот шаблон внутри классов, инициализируйте карту лямбда статически, иначе вы платите n log(n) каждый раз, чтобы построить ее с нуля.

Здесь мы можем уйти с инициализацией {} переменной static: Статические переменные в методах класса, но мы могли бы также использовать описанные методы at: статические конструкторы в С++? Мне нужно инициализировать частные статические объекты

Необходимо было преобразовать захват lambda context [&] в аргумент, или это было бы undefined: const static auto lambda, используемое с захватом по ссылке

Пример, который производит тот же вывод, что и выше:

#include <functional>
#include <iostream>
#include <map>
#include <string>

class RangeSwitch {
public:
    void method(int x, std::string &ret) {
        static const std::map<int,std::function<void(std::string&)>> m{
            {0, [](std::string &ret){ ret = "too small"; }},
            {2, [](std::string &ret){ ret = "[0,2)"; }},
            {5, [](std::string &ret){ ret = "[2,5)"; }},
            {7, [](std::string &ret){ ret = "[5,7)"; }},
        };
        static const auto end = m.end();
        auto it = m.upper_bound(x);
        if (it == end) {
            ret = "too large";
        } else {
            it->second(ret);
        }
    }
};

int main() {
    RangeSwitch rangeSwitch;
    std::string ret;
    for (auto i = -1; i < 8; ++i) {
        rangeSwitch.method(i, ret);
        std::cout << i << " " << ret << std::endl;
    }
}

Ответ 9

Это просто не так, как работает коммутатор. Он принимает только одно значение. Вам нужно будет использовать блоки if-elseif

Ответ 10

У меня была та же проблема с проблемой, основанной на оценке, и в то время как операторы if if elseif были полезны для использования. Для интервалов я обнаружил, что лучший вариант (для меня хотя бы потому, что мне нравится, как он выглядит, и это проще для я как новичок, чтобы увидеть свои ошибки) - "1... 10". но не забудьте использовать пробел между числом и точками или программа подумает, что ваш интервал - это число, и u получит ошибку "2 много десятичных точки...". Надеюсь, что это поможет.

int score;

int main()
{
    cout<<"Enter score"<<endl;
    cin>>score;

  switch(score){
    case 100:
        cout<<"Your score is Perfect"<<endl;
    break;
    case 90 ... 99:
        cout<<"You got A"<<endl;
    break;
    case 80 ... 89:
        cout<<"You got B"<<endl;
        break;
    case 70 ... 79:
        cout<<"You got C"<<endl;
        break;
    case 60 ... 69:
        cout<<"You got D"<<endl;
        break;
    case 50 ... 59:
        cout<<"You got E"<<endl;
        break;
    case 0 ... 49:
        cout<<"You got F"<<endl;}

  }

Ответ 11

Операторы case-оператора заменяют выражения long, которые сравнивают переменную с несколькими "интегральными" значениями ( "интегральные" значения - это просто значения, которые могут быть выражены как целое число, такое как значение a char). Условием оператора switch является значение. В этом случае говорится, что если у него есть значение того, что есть после этого случая, то делайте то, что следует за двоеточием. Разрыв используется для выхода из операторов case.

Следовательно, вы не можете использовать такие условные операторы в случае.

Селективная структура: переключатель

Ответ 12

Это то, что сработало для меня. разделив метку на 10, а затем установив регистр 10 и 9, чтобы отобразить "A" (это отобразит "A" для любого значения между 90-100. Затем в случае 8 для отображения "B", тогда в случае 7 появится "C" для значений от 70 до 79 и т.д.

#include <iostream>
using namespace std;

main ()
{
    int mark;
    cout << "enter your mark: ";
    cin >> mark;
    switch (mark/10)
    {
        case 10: case 9: cout << "A"; break;
        case 8: cout << "B"; break;
        case 7: cout << "C"; break;
        case 6: cout << "D"; break;
        case 5: cout << "PASS"; break;
        default: cout << "FAIL"; break;
    }
}

Ответ 13

Вы можете сделать следующее:

//summarize the range to one value
If score < 0
    score = -1

switch(score){
case 1:
    //...
    break;
case 2:
    //...
    break;
case -1:    //complete neg. range
    //...
    break;
//...

}

Ответ 14

Здесь, на мой взгляд, выразительно и просто следовать.

Вы можете быть удивлены тем, насколько gcc/clang и т.д. могут оптимизировать код, который он создает. Я ожидаю, что он будет по меньшей мере столь же эффективным, как и коммутатор/случай.

#include <iostream>

template<class Value>
struct switcher
{
    constexpr switcher(Value const& value) : value_(value) {}
    constexpr switcher(Value const& value, bool enabled) : value_(value), enabled(enabled) {}

    template<class From, class To, class F>
    constexpr auto in_range(From&& from, To&& to, F&& f)
    {
        if (enabled and (from <= value_ and value_ <= to))
        {
            f();
            return switcher(value_, false);
        }
        else {
            return *this;
        }
    };

    template<class F>
    constexpr auto otherwise(F&& f)
    {
        if (enabled)
            f();
    }

    Value const& value_;
    const bool enabled = true;
};

template<class Value>
constexpr auto decision(Value const& value)
{
    return switcher<Value>(value);
}

void test(int x)
{
    decision(x)
            .in_range(0, 10, [&] { std::cout << x << " maps to option A\n"; })
            .in_range(11, 20, [&] { std::cout << x << " maps to option B\n"; })
            .otherwise([&] { std::cout << x << " is not covered\n"; });
}

int main(int argc, char **argv) {

    test(5);
    test(14);
    test(22);
}

Ответ 15

Потенциально полезная информация заключается в том, что switch принимает выражение, поэтому вы можете сбрасывать несколько входных значений до одного случая коммутатора. Это большой уродливый, но для рассмотрения:

switch (score / 10)
{
  case 10:
    cout << "a";
    break;

  case 9: case 8: case 7: case 6: case 5:
    cout << "b";
    break;

  case 4: case 3:
    cout << "c";
    break;

  case 2:
    if (score >= 25)
    {
        cout << "c";
        break;
    }
    // else fall through...
  case 1:
    cout << "d";
    break;

  case 0:
    cout << (score > 0 ? "e" : "f");
    break;

  default:
    cout << "BAD VALUE";
    break;
}

Конечно, вы могли бы разделить на 5 и имели case 4: (для 20-24) вместо case 5: (25-29), а не if внутри case 2:, но /10, возможно, больше интуитивным.

Ответ 16

Я знаю, что это старый квест, но поскольку switch заявления на самом деле являются обертками вокруг ярлыков, я считаю, что goto может быть полезным.

    int value = 40;
    if (value < 10) {
        std::cout << "value < 10" << std::endl;
        goto end;
    }
    if (value < 50) {
        std::cout << "value < 50" << std::endl;
        goto end;
    }
    if (value > 30) {
        std::cout << "value > 30" << std::endl;
        goto end;
    }
end:
    // resume

Таким образом, вы можете опустить все else и сохранить его компактным. Вы должны быть осторожны при использовании goto, хотя (в общем).

Ответ 17

Что-то вроде этого?

case 'A'..'Z' where a not in ['I','L','O']:

К сожалению, ни один компилятор, которого я знаю, не реализует это конкретное расширение, хотя GCC может делать диапазоны, как указывали другие ответы. Для переносимости вы можете вырезать и вставлять этот лицензионный фрагмент DWTFYW. Если вы используете пользовательское перечисление, вы можете прибегнуть к генерации кода, чтобы сделать что-то подобное.

#define CASE_NUMBER \
 case'0':case'1':case'2':case'3':case'4':\
 case'5':case'6':case'7':case'8':case'9'
#define CASE_ALPHA_LOWER \
 case'a':case'b':case'c':case'd':\
 case'e':case'f':case'g':case'h':\
 case'i':case'j':case'k':case'l':\
 case'm':case'n':case'o':case'p':\
 case'q':case'r':case's':case't':\
 case'u':case'v':case'w':case'x':\
 case'y':case'z'
#define CASE_ALPHA_UPPER \
 case'A':case'B':case'C':case'D':\
 case'E':case'F':case'G':case'H':\
 case'I':case'J':case'K':case'L':\
 case'M':case'N':case'O':case'P':\
 case'Q':case'R':case'S':case'T':\
 case'U':case'V':case'W':case'X':\
 case'Y':case'Z'
#define CASE_ALPHA CASE_ALPHA_UPPER:CASE_ALPHA_LOWER
#define CASE_ALPHANUM CASE_ALPHA:CASE_NUMBER

Если вы получаете доступ к GHCI, такой как онлайн-версия, https://ghc.io/, вы можете просто создать то, что вам нужно, и вставить его в заголовок, например

foldl (++) "" ["case" ++ show x ++ ":" | x <- ['A'..'Z'], not $ x `elem` ['I','L','O']]