web-dev-qa-db-ja.com

switchステートメントで値の範囲を選択するにはどうすればよいですか?

コンパイルしようとすると、次のエラーが表示されます。

 1> ------ビルド開始:プロジェクト:snake、構成:Debug Win32 ------ 
 1> Exercise.cpp 
 1> c:\ users\robin\documents\visual studio 2010\projects\snake\snake\exercise.cpp(13):エラーC2059:構文エラー: '> =' 
 1> c:\ users\robin\documents\visual studio 2010\projects\snake\snake\exercise.cpp(16):エラーC2059:構文エラー: '> =' 
 1> c:\ users\robin\documents\visual studio 2010\projects\snake\snake\exercise.cpp(19):エラーC2059:構文エラー: '> =' 
 1> c:\ users\robin\documents\visual studio 2010\projects\snake\snake\exercise.cpp(22 ):エラーC2059:構文エラー: '> =' 
 1> c:\ users\robin\documents\visual studio 2010\projects\snake\snake\exercise.cpp(25):エラーC2059:構文エラー: '>' 
 1> c:\ users\robin\documents\visual studio 2010\projects\snake\snake\exercise.cpp(28):エラーC2059:構文エラー: '==' 
 1> c:\ users\robin\documents\visual studio 2010\projects\snake\snake\exercise.cpp(34):警告C4065:switchステートメントには「default」buが含まれますt 'case'ラベルなし
 ==========ビルド:0成功、1失敗、0最新、0スキップ========== 

コード:

#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およびmy IDEはWindows Enterprise C++ 2010です。

ゲームプログラミングによるC++の開始から学んでいます。

20

C++の場合、ラベルは定数式であり、一般的な式ではありません。あなたがやろうとしていることを行うには、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...

}
25
dasblinkenlight

一部のコンパイラは、C++言語のextensionとしてcase x ... yのようなcase範囲をサポートします。

例:

#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/C++ Compiler 13.0.1はそれをサポートしているようです( http://gcc.godbolt.org/ で試しました)。一方、Visual C++ 19は( http://webcompiler.cloudapp.net/ で試しました)しません。

51
Ankit Patel

一連のif/else ifステートメントを使用して、この問題を修正できます。 Switch/caseは、C++ではこのように使用できません。

18
mah

switchstd::mapを使用して実行できます。

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

GCC拡張 があり、それはまさにあなたが望むことをします。

5
Janus Troelsen

C++では、switchステートメントは定数整数値のみに一致します。

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 は_lower_bound_に言及していますが、enumをラムダ(または継承しない場合は継承)で取り除くこともできます持っている)。

_#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メソッド変数の_{}_初期化を回避できます。 クラスメソッドの静的変数 ですが、以下で説明するメソッドも使用できます。 C++の静的コンストラクタ?プライベート静的オブジェクトを初期化する必要がある

ラムダコンテキストキャプチャ_[&]_を引数に変換する必要がありました。または、それは未定義でした: 参照によるキャプチャで使用される静的な自動ラムダ

上記と同じ出力を生成する例:

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

標準ではこれが許可されていません。

6.4.2 switchステートメント[stmt.switch]

[...] switchステートメント内のステートメントには、次のように1つ以上のcaseラベルを付けることができます。

case constant-expression :

ここで、constant-expressionは整数定数式(5.19)でなければなりません。

言い換えると、単一の整数の「ハード」コンパイル時定数に展開されるケース値のみを使用できます(例:case 5+6:enum {X = 3}; ... case X*X:)。

これを回避する方法は、if-ステートメントを使用することです。たとえば、交換する

switch (x)
case 0..100:

代わりに

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

3
Sebastian Mach

Switch-caseは、範囲をテストするための優れたオプションではありません。最良のオプションは、いくつかの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";

実行時間が問題になる場合は、次のソリューションの方が高速です。

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";
3
Fifi

私はスコアベースの問題で同じ問題を抱えていましたが、「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;}

  }
2
Bubu

それは単にスイッチの仕組みではありません。単一の値のみを取ります。 if-elseifブロックを使用する必要があります

2
KitsuneYMG

これは私のために働いたものです。マークを10で割ってから、ケース10と9に「A」を表示するように設定します(90〜100の任意の値に対して「A」が表示されます。その後、ケース8が「B」を表示し、ケース7 70〜79の値のC」など。

#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

表現力があり、従うのが簡単な方法を以下に示します。

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

次のことができます。

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

Switch caseステートメントは、変数を複数の「整数」値と比較するlong ifステートメントの代わりになります(「整数」値は、charの値などの整数として表現できる値です)。 switchステートメントの条件は値です。このケースでは、そのケースの後の値があれば、コロンに続くものは何でもするという。 breakは、caseステートメントから抜け出すために使用されます。

したがって、そのような条件付きステートメントを使用することはできません。

選択的構造:スイッチ

1
Taha

このようなもの?

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

https://ghc.io/ のオンラインバージョンなどのGHCIにアクセスする場合は、必要なものを生成して、ヘッダーに貼り付けるだけです。

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

これは古い質問ですが、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

この方法で、すべてのelsesを省略してコンパクトに保つ​​ことができます。 (一般的に)gotoを使用する場合は注意が必要です。

0
mewa

潜在的に有用な洞察は、switchが式を受け入れるため、複数の入力値を1つのスイッチケースにフォールドできることです。それは大きないですが、考慮のために:

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:内のifではなくcase 2:(25-29)を使用することもできますが、/10間違いなくより直感的です。

0
Tony Delroy