Я просто заметил, что вы не можете использовать стандартные математические операторы для перечисления, такие как ++ или + =Как я могу перебирать перечисление?
Так что же лучший способ перебора всех значений в перечислении C++?
Я просто заметил, что вы не можете использовать стандартные математические операторы для перечисления, такие как ++ или + =Как я могу перебирать перечисление?
Так что же лучший способ перебора всех значений в перечислении C++?
Типичный способ заключается в следующем:
enum Foo {
One,
Two,
Three,
Last
};
for (int fooInt = One; fooInt != Last; fooInt++)
{
Foo foo = static_cast<Foo>(fooInt);
// ...
}
Конечно, это ломается, если заданы значения перечислений:
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;
}
Если вы действительно хотите перечислить, введите значения enum в вектор и перейдете к этому. Это также будет иметь дело с указанными значениями перечисления.
Обратите внимание, что в первой части примера, если вы хотите использовать «i» в качестве перечисления Foo, а не int, вам нужно будет статично отличить его: static_cast
Также вы пропустите Последнее в петля. Должно быть <= Last – Tony 2013-06-13 01:24:19
@Tony Last предназначен для пропуска. Если вы хотите добавить больше перечислений позже, добавьте их до последнего ... цикл в первом примере все равно будет работать. Используя «поддельное» последнее перечисление, вам не нужно обновлять условие завершения в цикле for до последнего «реального» перечисления каждый раз, когда вы хотите добавить новое перечисление. – timidpueo 2013-08-03 21:06:08
Вы не можете с перечислением. Возможно, перечисление не подходит для вашей ситуации.
Общее соглашение - это имя последнего значения перечисления, что-то вроде MAX, и использовать его для управления циклом с использованием int.
У C++ нет интроспекции, поэтому вы не можете определить этот тип вещей во время выполнения.
Если перечисление начинается с 0 и приращение всегда 1.
enum enumType
{
A = 0,
B,
C,
enumTypeEnd
};
for(int i=0; i<enumTypeEnd; i++)
{
enumType eCurrent = (enumType) i;
}
Если нет, я думаю, только почему, чтобы создать что-то вроде
vector<enumType> vEnums;
добавить элементы, а также использование нормальные итераторы ....
Один из многих подходов: When enum Just Isn't Enough: Enumeration Classes for C++.
И, если вы хотите что-то более инкапсулированное, попробуйте this approach от James Kanze.
Вы также можете перегрузить операторы increment/decment для вашего перечисленного типа.
Вы можете попытаться определить следующий макрос:
#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++. Код чрезвычайно эффективен. Не откладывайте, а три для операторов: компилятор будет производить только один цикл после оптимизации! Вы даже можете определить закрываемую петлю:
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;
}
Вы, очевидно, не можете перебирать перечисленные тип с пробелами.
Что-то, что не было рассмотрено в других ответах = если вы используете строго типизированные C++ 11 перечислений, вы не можете использовать ++
или + int
на них. В этом случае немного раствора Messier требуется:
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)
}
слишком сложно эти решения, я делаю так:
enum NodePosition { Primary = 0, Secondary = 1, Tertiary = 2, Quaternary = 3};
const NodePosition NodePositionVector[] = { Primary, Secondary, Tertiary, Quaternary };
for (NodePosition pos : NodePositionVector) {
...
}
#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;
И тогда вы можете перебирать с помощью ра nge-for
for (foo i : fooIterator()) { //notice the parenteses!
do_stuff(i);
}
Предполагается, что у вас нет пробелов в вашем перечислении, все еще верно; нет никаких предположений о количестве битов на самом деле необходимы для хранения значения перечисления (благодаря StD :: underlying_type)
Для компиляторов 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);
}
Примечание: это намного меньше кода, чем просто templatized пользовательский ответ итератора.
Вы можете заставить это работать с GCC с помощью typeof
вместо decltype
, но на данный момент у меня нет такого компилятора, чтобы убедиться, что он скомпилирован.
Если вы не хотите загрязнять вас перечислением конечным пунктом 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: ключ перечисления, вы можете:
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);
}
}
Он работает, как ожидалось.
Я часто делаю это как то
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 EMyEnum
{
E_First,
E_None = E_First,
E_Green = 0x1,
E_White = 0x2
E_Blue = 0x4,
E_Last
}
for (EMyEnum i = E_First; i < E_Last; i = EMyEnum(i << 1))
{}
Один из ответов говорит: «Если бы вы знали, что значения enum были последовательными, например, перечисление Qt: Key ".
Qt :: Значения ключей не являются последовательными.Некоторые сегменты перечислены.
Эта статья посвящена итерации по всем значениям в перечислении. На самом деле это возможно в Qt из-за его использования Meta System Object:
const QMetaObject *metaObject = qt_getQtMetaObject();
QMetaEnum keyEnum = metaObject->enumerator(metaObject->indexOfEnumerator("Key"));
for (int i = 0; i < keyEnum.keyCount(); ++i) {
qDebug() << keyEnum.key(i);
}
Смотрите также QObject :: метаобъект() и Q_ENUM макро.
Я думаю, что этот материал станет проще с C++ 20? Но я не заглянул в нее.
Связанные предметы имеют некоторые интересные ответы. – Tony 2013-06-13 01:27:31
Эти ответы, похоже, не охватывают проблему, что `int` может быть недостаточно большой! (`[C++ 03: 7.2/5]`) – 2013-06-21 10:03:56
Интересно, что вы можете определить `operator ++` на enums; однако, вы можете сделать `for (Enum_E e = (Enum_E) 0; e
weberc2
2014-06-25 22:52:54