web-dev-qa-db-ja.com

列挙型を反復処理する方法

私はあなたが++や+ =のようなenumに標準の数学演算子を使うことができないことに気づいた

それでは、C++列挙型のすべての値を反復処理する最善の方法は何ですか?

263
Adam

一般的な方法は次のとおりです。

enum Foo {
  One,
  Two,
  Three,
  Last
};

for ( int fooInt = One; fooInt != Last; fooInt++ )
{
   Foo foo = static_cast<Foo>(fooInt);
   // ...
}

Enum値が指定されている場合はもちろん、これは故障します。

enum Foo {
  One = 1,
  Two = 9,
  Three = 4,
  Last
};

これは、enumが実際に反復することを意図していないことを示しています。列挙型を扱う典型的な方法は、それをswitch文で使用することです。

switch ( foo )
{
    case One:
        // ..
        break;
    case Two:  // intentional fall-through
    case Three:
        // ..
        break;
    case Four:
        // ..
        break;
     default:
        assert( ! "Invalid Foo enum value" );
        break;
}

本当に列挙したい場合は、enum値をベクトルに詰め込み、それを反復します。これは指定されたenum値も適切に処理します。

234
andreas buykx
#include <iostream>
#include <algorithm>

namespace MyEnum
{
  enum Type
  {
    a = 100,
    b = 220,
    c = -1
  };

  static const Type All[] = { a, b, c };
}

void fun( const MyEnum::Type e )
{
  std::cout << e << std::endl;
}

int main()
{
  // all
  for ( const auto e : MyEnum::All )
    fun( e );

  // some
  for ( const auto e : { MyEnum::a, MyEnum::b } )
    fun( e );

  // all
  std::for_each( std::begin( MyEnum::All ), std::end( MyEnum::All ), fun );

  return 0;
}
33
ZDF

列挙型が0から始まり、増分が常に1の場合.

enum enumType 
{ 
    A = 0,
    B,
    C,
    enumTypeEnd
};

for(int i=0; i<enumTypeEnd; i++)
{
   enumType eCurrent = (enumType) i;            
}

そうでない場合、私は唯一の理由はのようなものを作成することであると思います

vector<enumType> vEnums;

アイテムを追加して、通常のイテレータを使用してください。

18
João Augusto

C++ 11では、実際には代替手段があります。単純なテンプレート化されたカスタムイテレータを書くことです。

あなたの列挙型が

enum class foo {
  one,
  two,
  three
};

このジェネリックコードは、非常に効率的にトリックを実行します。ジェネリックヘッダーに配置すると、反復する必要がある可能性があるすべての列挙型に役立ちます。

#include <type_traits>
template < typename C, C beginVal, C endVal>
class Iterator {
  typedef typename std::underlying_type<C>::type val_t;
  int val;
public:
  Iterator(const C & f) : val(static_cast<val_t>(f)) {}
  Iterator() : val(static_cast<val_t>(beginVal)) {}
  Iterator operator++() {
    ++val;
    return *this;
  }
  C operator*() { return static_cast<C>(val); }
  Iterator begin() { return *this; } //default ctor is good
  Iterator end() {
      static const Iterator endIter=++Iterator(endVal); // cache it
      return endIter;
  }
  bool operator!=(const Iterator& i) { return val != i.val; }
};

あなたはそれを特殊化する必要があるでしょう

typedef Iterator<foo, foo::one, foo::three> fooIterator;

そして、range-forを使って繰り返します。

for (foo i : fooIterator() ) { //notice the parentheses!
   do_stuff(i);
}

あなたはあなたのenumにギャップがないという仮定はまだ真実です。 enum値を格納するために実際に必要なビット数についての仮定はありません(std :: layered_typeのおかげで)

15

あまりにも複雑なこれらの解決策、私はそれが好きです:

enum NodePosition { Primary = 0, Secondary = 1, Tertiary = 2, Quaternary = 3};

const NodePosition NodePositionVector[] = { Primary, Secondary, Tertiary, Quaternary };

for (NodePosition pos : NodePositionVector) {
...
}
13
Enzojz

あなたは列挙型ではできません。たぶんenumはあなたの状況に最適ではありません。

一般的な慣例では、最後のenum値にMAXのような名前を付け、それをintを使用してループを制御するために使用します。

8
Corey Trager

私はしばしばそれを好む

    enum EMyEnum
    {
        E_First,
        E_Orange = E_First,
        E_Green,
        E_White,
        E_Blue,
        E_Last
    }

    for (EMyEnum i = E_First; i < E_Last; i = EMyEnum(i + 1))
    {}

連続していなくても、通常のステップ(ビットフラグなど)がある場合

    enum EAnimal
    {
        E_First,
        E_None    = E_First,
        E_CanFly  = 0x1,
        E_CanWalk = 0x2
        E_CanSwim = 0x4,
        E_Last
    }

    for (EAnimali = E_First; i < E_Last; i = EAnimal(i << 1))
    {}
7
Niki

他の答えでカバーされていない何か=あなたが強く型付けされたC++ 11列挙型を使っているならば、あなたはそれらに+++ intを使うことができません。その場合、少し面倒な解決策が必要です。

enum class myenumtype {
  MYENUM_FIRST,
  MYENUM_OTHER,
  MYENUM_LAST
}

for(myenumtype myenum = myenumtype::MYENUM_FIRST;
    myenum != myenumtype::MYENUM_LAST;
    myenum = static_cast<myenumtype>(static_cast<int>(myenum) + 1)) {

  do_whatever(myenum)

}
6
Riot

次のマクロを試して定義することができます。

#define for_range(_type, _param, _A1, _B1) for (bool _ok = true; _ok;)\
for (_type _start = _A1, _finish = _B1; _ok;)\
    for (int _step = 2*(((int)_finish)>(int)_start)-1;_ok;)\
         for (_type _param = _start; _ok ; \
 (_param != _finish ? \
           _param = static_cast<_type>(((int)_param)+_step) : _ok = false))

今、あなたはそれを使うことができます:

enum Count { zero, one, two, three }; 

    for_range (Count, c, zero, three)
    {
        cout << "forward: " << c << endl;
    }

符号なし整数、列挙型、および文字による逆方向および逆方向の繰り返しに使用できます。

for_range (unsigned, i, 10,0)
{
    cout << "backwards i: " << i << endl;
}


for_range (char, c, 'z','a')
{
    cout << c << endl;
}

その厄介な定義にもかかわらず、それは非常にうまく最適化されています。私はVC++で逆アセンブラを見ました。コードは非常に効率的です。先延ばしにしてはいけませんが、3つのfor文だけです。コンパイラは最適化後に1つのループしか生成しません。囲まれたループも定義できます。

unsigned p[4][5];

for_range (Count, i, zero,three)
    for_range(unsigned int, j, 4, 0)
    {   
        p[i][j] = static_cast<unsigned>(i)+j;
    }

あなたは明らかにギャップのある列挙型を通して反復することはできません。

6
Mikhail Semenov

列挙型に対してインクリメント/デクリメント演算子をオーバーロードすることもできます。

4
JohnMcG

もしあなたが最後のCOUNT項目であなたがenumを汚染したくないのであれば(おそらくもしあなたがスイッチの中でenumを使っているのであれば、コンパイラーはCOUNTがないことを警告するでしょう:)、これをすることができます:

enum Colour {Red, Green, Blue};
const Colour LastColour = Blue;

Colour co(0);
while (true) {
  // do stuff with co
  // ...
  if (co == LastColour) break;
  co = Colour(co+1);
}
2
Niels Holst

MSコンパイラの場合

#define inc_enum(i) ((decltype(i)) ((int)i + 1))

enum enumtype { one, two, three, count};
for(enumtype i = one; i < count; i = inc_enum(i))
{ 
    dostuff(i); 
}

注:これは単純なテンプレート化されたカスタムイテレータの回答よりもはるかに少ないコードです。

typeofの代わりにdecltypeを使用することでGCCでこれを機能させることができますが、コンパイルを確実にするために現時点ではそのコンパイラーは役に立ちません。

1
user2407277

Enumに順番に番号が付けられていると仮定すると、エラーが発生しやすくなります。さらに、選択した列挙子のみを反復処理することもできます。そのサブセットが小さい場合、明示的にループすることはエレガントな選択かもしれません:

enum Item { Man, Wolf, Goat, Cabbage }; // or enum class

for (auto item : {Wolf, Goat, Cabbage}) { // or Item::Wolf, ...
    // ...
}
1
marski

これは、連続した列挙型に対してのみ機能するもう1つの解決策です。それはC++では壊れているのでそれが属するところである増分の醜さを除いて、それは予想される繰り返しを与えます。

enum Bar {
    One = 1,
    Two,
    Three,
    End_Bar // Marker for end of enum; 
};

for (Bar foo = One; foo < End_Bar; foo = Bar(foo + 1))
{
    // ...
}
1
Ethan Bradford

Enumの値がシーケンシャルであることを知っていたら、例えばQt:Key enumなら、

Qt::Key shortcut_key = Qt::Key_0;
for (int idx = 0; etc...) {
    ....
    if (shortcut_key <= Qt::Key_9) {
        fileMenu->addAction("abc", this, SLOT(onNewTab()),
                            QKeySequence(Qt::CTRL + shortcut_key));
        shortcut_key = (Qt::Key) (shortcut_key + 1);
    }
}

期待通りに動作します。

0
kcrossen
typedef enum{
    first = 2,
    second = 6,
    third = 17
}MyEnum;

static const int enumItems[] = {
    first,
    second,
    third
}

static const int EnumLength = sizeof(enumItems) / sizeof(int);

for(int i = 0; i < EnumLength; i++){
    //Do something with enumItems[i]
}
0
Justin Moloney

C++にはイントロスペクションがないため、実行時にこのようなことを判断することはできません。

0
David Kemp
enum class A {
    a0=0, a3=3, a4=4
};
constexpr std::array<A, 3> ALL_A {A::a0, A::a3, A::a4}; // constexpr is important here

for(A a: ALL_A) {
  if(a==A::a0 || a==A::a4) std::cout << static_cast<int>(a);
}

constexpr std::arrayは、配列がコンパイラによってインスタンス化されていなくても、非順次のenumでも反復できます。これは、コンパイラの最適化ヒューリスティックや配列のアドレスを取るかどうかなどによって異なります。

私の実験では、2つの非連続値またはかなり少ない数の連続値がある場合、g++を伴う-O3 9.1が上記の配列を最適化することがわかりました(私は6つまでテストしました)。しかし、これはifステートメントがある場合にのみ行われます。 (シーケンシャル配列内のすべての要素より大きい整数値を比較し、何も除外されていないにもかかわらず反復をインライン化しましたが、ifステートメントを省略すると、値がメモリに書き込まれました。) [1つのケース| https://godbolt.org/z/XuGtoc] の中の非連続enumからの値。私は、この奇妙な振る舞いがキャッシュと分岐予測に関係した深い発見的方法によるものだと思います。

これは godboltでの単純なテスト反復へのリンク です。これは配列が常にインスタンス化されるわけではないことを示しています。

この手法の代償は、enum要素を2回書いて2つのリストを同期させることです。

0
Eponymous