++や+=などの列挙型で標準の数学演算子を使用できないことに気づきました
では、C ++列挙型のすべての値を反復処理するための最良の方法は何ですか?
典型的な方法は次のとおりです。
enum Foo {
One,
Two,
Three,
Last
};
for ( int fooInt = One; fooInt != Last; fooInt++ )
{
Foo foo = static_cast<Foo>(fooInt);
// ...
}
列挙型Last
は反復によってスキップされることを意図していることに注意してください。この「偽の」Last
列挙型を利用すると、新しい列挙型を追加するたびに for ループの終了条件を最後の「本物の」列挙型に更新する必要がなくなります。後で列挙型を追加する場合は、Last の前に追加します。この例のループは引き続き機能します。
もちろん、列挙値が指定されている場合、これは失敗します。
enum Foo {
One = 1,
Two = 9,
Three = 4,
Last
};
これは、列挙型が実際には反復処理を意図していないことを示しています。列挙型を処理する一般的な方法は、switch ステートメントで使用することです。
switch ( foo )
{
case One:
// ..
break;
case Two: // intentional fall-through
case Three:
// ..
break;
case Four:
// ..
break;
default:
assert( ! "Invalid Foo enum value" );
break;
}
本当に列挙したい場合は、列挙値をベクトルに詰め込み、それを反復処理します。これにより、指定された列挙値も適切に処理されます。
#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;
}
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);
}
列挙型にギャップがないという仮定は依然として当てはまります。列挙値を格納するために実際に必要なビット数についての仮定はありません (std::underlying_type のおかげです)。
列挙型が 0 で始まり、増分が常に 1 の場合。
enum enumType
{
A = 0,
B,
C,
enumTypeEnd
};
for(int i=0; i<enumTypeEnd; i++)
{
enumType eCurrent = (enumType) i;
}
そうでない場合、唯一の理由は、のようなものを作成することだと思います
vector<enumType> vEnums;
アイテムを追加し、通常のイテレータを使用します....
列挙型ではできません。たぶん、列挙型はあなたの状況に最適ではありません。
一般的な規則は、最後の列挙値に MAX のような名前を付け、それを使用して int を使用してループを制御することです。
++
他の回答でカバーされていないもの = 強く型付けされた C++11 列挙型を使用している場合、それらに対してorを使用することはできません+ 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)
}
次のマクロを試して定義できます。
#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;
}
unsigned、integer、enum、およびcharを前後に反復するために使用できます。
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;
}
明らかに、ギャップのある列挙型を反復処理することはできません。
列挙型のインクリメント/デクリメント演算子をオーバーロードすることもできます。
最後の COUNT アイテムで列挙型を汚染したくない場合 (スイッチで列挙型も使用すると、コンパイラはケース 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);
}
Qt:Key 列挙型など、列挙型の値が連続していることがわかっている場合は、次のことができます。
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);
}
}
期待どおりに動作します。
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]
}
ほとんどのソリューションは (MIN, MAX) 範囲のループに基づいていますが、列挙型の穴である可能性があるという事実を見落としています。
私の提案は次のとおりです。
for (int i = MYTYPE_MIN; i <= MYTYPE_MAX; i++) {
if (MYTYPE_IsValid(i)) {
MYTYPE value = (MYTYPE)i;
// DoStuff(value)
}
}
C++ にはイントロスペクションがないため、この種のことを実行時に判断することはできません。
int の配列を作成して配列をループするだけですが、最後の要素を -1 にして終了条件に使用します。
列挙型の場合:
enum MyEnumType{Hay=12,Grass=42,Beer=39};
次に配列を作成します。
int Array[] = {Hay,Grass,Beer,-1};
for (int h = 0; Array[h] != -1; h++){
doStuff( (MyEnumType) Array[h] );
}
もちろん、-1チェックが要素の1つと衝突しない限り、表現のintに関係なく、これは崩壊しません。