我只是注意到您不能在++或+ =等枚举上使用标准数学运算符
那么,迭代C ++枚举中的所有值的最佳方法是什么?
int
可能还不够大的问题!([C++03: 7.2/5]
)
operator++
枚举。但是,您可以这样做for(Enum_E e = (Enum_E)0; e < ENUM_COUNT; e++)
。请注意,您必须强制转换0
为Enum_E
C ++禁止对枚举进行赋值运算符。
我只是注意到您不能在++或+ =等枚举上使用标准数学运算符
那么,迭代C ++枚举中的所有值的最佳方法是什么?
int
可能还不够大的问题!([C++03: 7.2/5]
)
operator++
枚举。但是,您可以这样做for(Enum_E e = (Enum_E)0; e < ENUM_COUNT; e++)
。请注意,您必须强制转换0
为Enum_E
C ++禁止对枚举进行赋值运算符。
Answers:
典型的方法如下:
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;
}
如果您真的想枚举,请将枚举值填充到向量中并对其进行迭代。这也将正确处理指定的枚举值。
UNKNOWN = 0
。另外,我建议default
在切换枚举值时只删除这种情况,因为它可能隐藏在运行时忘记处理值的情况。相反,应该对所有值进行硬编码,并使用该UNKNOWN
字段检测不兼容性。
#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;
}
如果您的枚举从0开始且增量始终为1。
enum enumType
{
A = 0,
B,
C,
enumTypeEnd
};
for(int i=0; i<enumTypeEnd; i++)
{
enumType eCurrent = (enumType) i;
}
如果不是,我想唯一的原因就是要创建一个像
vector<enumType> vEnums;
添加项目,并使用普通的迭代器。
对于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)
std::vector
不是通用的,因为std::vector<foo>
它与绑在一起foo
。
typedef Iterator<color, color::green, color::red> colorIterator;
确保您了解模板实例化的工作方式。
foo operator*() { ...
应该是C operator*() { ...
。
这些解决方案太复杂了,我这样做:
enum NodePosition { Primary = 0, Secondary = 1, Tertiary = 2, Quaternary = 3};
const NodePosition NodePositionVector[] = { Primary, Secondary, Tertiary, Quaternary };
for (NodePosition pos : NodePositionVector) {
...
}
for (NodePosition pos : NodePositionVector)
语法?据我所知,这是Java语法,您将需要C ++中的迭代器来执行等效操作。
auto
,因此您不必显式声明元素的类型,除非您(A)需要使用转换运算符或(B)auto
由于某种原因不喜欢。大多数范围for
用户使用auto
AFAICT
我经常那样做
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 EAnimalCaps
{
E_First,
E_None = E_First,
E_CanFly = 0x1,
E_CanWalk = 0x2
E_CanSwim = 0x4,
E_Last
}
class MyAnimal
{
EAnimalCaps m_Caps;
}
class Frog
{
Frog() :
m_Caps(EAnimalCaps(E_CanWalk | E_CanSwim))
{}
}
for (EAnimalCaps= E_First; i < E_Last; i = EAnimalCaps(i << 1))
{}
其他答案中未涉及的内容=如果您使用的是强类型的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)
}
您可以尝试定义以下宏:
#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 ++中的反汇编程序。该代码非常有效。不要拖延,而是三个for语句:优化后,编译器只会产生一个循环!您甚至可以定义封闭的循环:
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;
}
您显然无法遍历带有间隙的枚举类型。
_A1
不是允许使用的名称,它是带下划线的前导下划线。
您也可以为枚举类型重载递增/递减运算符。
假定枚举顺序编号是容易出错的。此外,您可能只想迭代选定的枚举数。如果该子集很小,则明确地对其进行循环可能是一个不错的选择:
enum Item { Man, Wolf, Goat, Cabbage }; // or enum class
for (auto item : {Wolf, Goat, Cabbage}) { // or Item::Wolf, ...
// ...
}
如果您不喜欢用最后的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);
}
这是另一个仅适用于连续枚举的解决方案。它提供了预期的迭代,但增量是丑陋的,因为增量是它所属的,因为这是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))
{
// ...
}
foo = Bar(foo + 1)
。
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);
}
A constexpr std::array
甚至可以迭代非顺序枚举,而无需由编译器实例化数组。这取决于诸如编译器的优化试探法以及是否采用数组的地址之类的问题。
在我的实验中,我发现如果存在2个非顺序值或相当多的顺序值(我测试了最多6个),则g++
9.1 with -O3
将优化上述数组。但这仅在您有if
发言权的情况下执行。(我尝试了一个比较整数值的语句,该整数值比顺序数组中的所有元素都大,并且尽管没有排除任何变量,但它内联了迭代,但是当我省略if语句时,这些值已放入内存中。)它也内联了5 [一种情况|]中来自非顺序枚举的值 https://godbolt.org/z/XuGtoc]。我怀疑这种奇怪的行为是由于深度启发法与缓存和分支预测有关。
这是一个指向Godbolt的简单测试迭代的链接,该迭代演示了数组并不总是被实例化。
这种技术的代价是两次编写枚举元素并使两个列表保持同步。
在Bjarne Stroustrup的C ++编程语言书中,您可以了解到他提议重载operator++
您的enum
。enum
是针对这些特定情况的用户定义类型和语言中存在的重载运算符。
您将能够编写以下代码:
#include <iostream>
enum class Colors{red, green, blue};
Colors& operator++(Colors &c, int)
{
switch(c)
{
case Colors::red:
return c=Colors::green;
case Colors::green:
return c=Colors::blue;
case Colors::blue:
return c=Colors::red; // managing overflow
default:
throw std::exception(); // or do anything else to manage the error...
}
}
int main()
{
Colors c = Colors::red;
// casting in int just for convenience of output.
std::cout << (int)c++ << std::endl;
std::cout << (int)c++ << std::endl;
std::cout << (int)c++ << std::endl;
std::cout << (int)c++ << std::endl;
std::cout << (int)c++ << std::endl;
return 0;
}
测试代码:http : //cpp.sh/357gb
记住我正在使用enum class
。代码也可以正常工作enum
。但是我更喜欢,enum class
因为它们是强类型的,可以防止我们在编译时犯错误。
enum
。这不是体系结构问题。我不相信2013年C ++是科幻小说。
对于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一起使用,但是目前我没有那个编译器可以确保它可以编译。
decltype
成为标准C ++ 大约5年后编写的,因此您不应该推荐typeof
古老的GCC 过时的代码。大概最近的海湾合作委员会处理decltype
得很好。还有其他问题:不鼓励使用C型强制转换,并且宏程序更糟。适当的C ++功能可以提供相同的通用功能。最好重写为使用static_cast
&模板函数:template <typename T> auto inc_enum(T const t) { return static_cast<T>(static cast<int>(t) + 1); }
。而且,非不需要强制转换enum class
。另外,运算符可以按enum
类型(TIL)重载
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]
}
constexpr static const int enumItems[]
C ++没有内省,因此您无法在运行时确定这种事情。