web-dev-qa-db-fra.com

Comment sélectionner une plage de valeurs dans une instruction switch?

Lorsque j'essaie de compiler, j'obtiens cette erreur:

 1> ------ La construction a commencé: Projet: serpent, Configuration: Debug Win32 ------
 1> exercise.cpp 
 1> c:\utilisateurs\robin\documents\visual studio 2010\projects\snake\snake\exercise.cpp (13): erreur C2059: erreur de syntaxe: '> =' 
 1> c:\utilisateurs\robin\documents\visual studio 2010\projects\snake\snake\exercise.cpp (16): error C2059: erreur de syntaxe: '> =' 
 1> c:\users\robin\documents\visual studio 2010\projects\snake\snake\exercise.cpp (19): error C2059: erreur de syntaxe: '> =' 
 1> c:\utilisateurs\robin\documents\visual studio 2010\projets\serpent\serpent\exercice.cpp (22): erreur C2059: erreur de syntaxe: '> =' 
 1> c:\utilisateurs\robin\documents\visual studio 2010\projets\snake\snake\exercise.cpp (25): erreur C2059: erreur de syntaxe: '>' .__> 1> c:\utilisateurs\robin\documents\visual studio 2010\projects\serpent\serpent\exercise.cpp (28): erreur C2059: erreur de syntaxe: '==' 
 1> c:\utilisateurs\robin\documents\visual studio 2010\projects\snake\snake\exercise.cpp (34): avertissement C4065: l'instruction switch contient des libellés "par défaut" mais aucun "cas" 
 ========== Bui ld: 0 a réussi, 1 a échoué, 0 à jour, 0 ignoré ========== 

Code:

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

Comment puis-je résoudre ce problème? C'est une application console, Win32 et mon IDE est Windows Enterprise C++ 2010.

J'apprends de Début du C++ via la programmation de jeux .

18
Robin Van den Broeck

En C++, les libellés de casse sont constant expressions, pas les expressions en général. Vous avez besoin d'une chaîne de déclarations if-then-else pour faire ce que vous essayez de faire.

Vous pouvez également énumérer les valeurs du commutateur. C’est un peu plus rapide (bien que cela n’importe pas dans des cas comme le vôtre), mais c’est beaucoup moins lisible:

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

}
26
dasblinkenlight

Certains compilateurs prennent en charge plages de casse comme case x ... y en tant que extension au langage C++.

Exemple: 

#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 et le compilateur Intel C/C++ 13.0.1 semblent le supporter (essayé sur http://gcc.godbolt.org/ ). D'autre part, Visual C++ 19 ne fonctionne pas (essayé sur http://webcompiler.cloudapp.net/ ).

47
Ankit Patel

Vous pouvez résoudre ce problème en utilisant une série d'instructions if/else if. Switch/case ne peut pas être utilisé comme cela en C++.

16
mah

Cela peut être fait en utilisant un std::map avec 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: ...
}
9
Slava

Il y a une extension GCC qui fait exactement ce que vous voulez.

5
Janus Troelsen

En C++, une instruction switch ne peut correspondre qu'à des valeurs entières constantes:

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

std::map::upper_bound + C++ 11 lambdas

https://stackoverflow.com/a/35460297/895245 mentionne le lower_bound, mais nous pouvons aussi nous débarrasser de la enum avec lambdas (ou un héritage si vous ne l'avez pas).

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

Sortie:

-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

Utilisation dans les méthodes avec static

Pour utiliser efficacement ce modèle à l’intérieur des classes, initialisez la mappe lambda statiquement, sinon vous payerez n log(n) à chaque fois pour le construire à partir de zéro.

Ici, nous pouvons nous échapper avec l'initialisation {} d'une variable de méthode static: Variables statiques dans les méthodes de classe , mais nous pourrions également utiliser les méthodes décrites dans: constructeurs statiques en C++? J'ai besoin d'initialiser des objets statiques privés

Il était nécessaire de transformer la capture de contexte lambda [&] en un argument, ou cela aurait été indéfini: const statique lambda automatique utilisé avec capture par référence

Exemple produisant le même résultat que ci-dessus:

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

La norme ne permet pas cela:

6.4.2 L'instruction switch [stmt.switch]

[...] Toute instruction dans l'instruction switch peut être étiquetée avec une ou plusieurs étiquettes de cas, comme suit:

case constant-expression :

où constante-expression doit être une expression constante intégrale (5.19).

En d'autres termes, vous ne pouvez utiliser que des valeurs de casse qui se développent en une seule constante de temps de compilation "dure", intégrale (par exemple, case 5+6:, enum {X = 3}; ... case X*X:).

La solution consiste à utiliser des instructions if-. Par exemple, pour remplacer

switch (x)
case 0..100:

vous auriez plutôt

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

.

3
Sebastian Mach

J'ai eu le même problème avec un problème basé sur le score et alors que les déclarations "if/elseif" étaient bonnes à utiliser, par intervalles, j'ai trouvé que la meilleure option (pour moi au moins parce que j'aime à quoi ça ressemble et c'est plus facile pour moi débutant à voir mes erreurs) est "1 ... 10". mais n'oubliez pas d'utiliser un espace entre le nombre et les points, sinon le programme pensera que votre intervalle est un nombre et vous obtiendrez une erreur "2 nombreux points décimaux ...". J'espère que ça aide.

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

  }
2
Bubu

Ce n'est tout simplement pas la façon dont l'interrupteur fonctionne. Cela ne prend que des valeurs uniques. Vous devrez utiliser des blocs if-elseif

2
KitsuneYMG

C'est ce qui a fonctionné pour moi. en divisant le repère par 10, puis en configurant les cases 10 et 9 pour afficher un "A" (ceci affichera un "A" pour toute valeur comprise entre 90 et 100. Ensuite, le cas 8 pour afficher "B", puis le cas 7, un " C "pour les valeurs de 70 à 79 et ainsi de suite.

#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;
    }
}
1
jimmy harry

Voici un moyen qui, j’espère, est expressif et simple à suivre.

Vous pourriez être surpris de voir à quel point gcc/clang, etc. peut optimiser le code qu'il génère. Je m'attendrais à ce qu'il soit au moins aussi efficace qu'un commutateur/boîtier.

#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);
}
1
Richard Hodges

Vous pouvez faire ce qui suit:

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

}

1
Heike

Les instructions case majuscule se substituent aux instructions long if comparant une variable à plusieurs valeurs "intégrales" (les valeurs "intégrales" sont simplement des valeurs pouvant être exprimées sous la forme d'un entier, telles que la valeur d'un caractère). La condition d'une instruction switch est une valeur. Le cas dit que si elle a la valeur de ce qui est après, alors faites ce qui suit les deux points. La pause est utilisée pour sortir des déclarations de cas.

Par conséquent, vous ne pouvez pas utiliser de telles instructions conditionnelles au cas où.

La structure sélective: switch

1
Taha

Switch-case n'est pas une bonne option pour tester les gammes. La meilleure option consiste à utiliser plusieurs if:

if (score<0) cout << "BAD VALUE";
if (score == 0)  cout << "f";
if (score>0 && score<10) cout << "e";
if (score>=10 && score <25) cout << "d";
if (score>=25 && score <50) cout << "c";
if (score>=50 && score <100) cout << "b";

Si le temps d'exécution pose problème, la solution suivante est plus rapide:

if (score == 0)  cout << "f";
else if (score<10) cout << "e";
else if (score <25) cout << "d";
else if (score <50) cout << "c";
else if (score <100) cout << "b";
else if (score>=100) cout << "a";
else cout << "BAD VALUE";
1
Imabot

Quelque chose comme ça?

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

Malheureusement, aucun compilateur que je connaisse n'implémente cette extension particulière, bien que GCC puisse effectuer des plages comme d'autres réponses l'ont souligné. Pour la portabilité, vous pouvez couper et coller ce fragment de code sous licence DWTFYW. Si vous utilisez une énumération personnalisée, vous pouvez recourir à la génération de code pour créer quelque chose de similaire.

#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

Si vous accédez à GHCI tel que la version en ligne sur https://ghc.io/ , vous pouvez simplement générer ce dont vous avez besoin et le coller dans un en-tête, par exemple.

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

Je sais que c’est une vieille question, mais comme les déclarations switch sont en fait des enveloppes autour des étiquettes, je trouve que goto peut être (bien) utile ici. 

    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

De cette façon, vous pouvez omettre tous les elses et le garder compact . Vous devez cependant faire attention lorsque vous utilisez goto (en général).

0
mewa

Une idée potentiellement utile est que switch accepte une expression, ce qui vous permet de plier plusieurs valeurs d'entrée en un seul cas de commutation. C'est un gros moche, mais pour considération:

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

Bien sûr, vous auriez pu diviser par 5 et avoir case 4: (pour 20-24) vs case 5: (25-29) plutôt qu’un if dans case 2:, mais /10 est sans doute plus intuitif.

0
Tony Delroy