我只是注意到不能在枚举上使用标准数学运算符,如++或+=。
那么,在c++枚举中遍历所有值的最佳方法是什么?
我只是注意到不能在枚举上使用标准数学运算符,如++或+=。
那么,在c++枚举中遍历所有值的最佳方法是什么?
当前回答
在Bjarne Stroustrup的c++编程语言书中,你可以读到他建议为特定的枚举重载操作符++。枚举是用户定义的类型,语言中存在针对这些特定情况的重载运算符。
你将能够编写以下代码:
#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
注意,我使用的是枚举类。Code也可以很好地使用enum。但我更喜欢枚举类,因为它们是强类型的,可以防止我们在编译时犯错误。
其他回答
将变量强制转换为int&类型可以在保持类型可读的同时进行递增。
#include <iostream>
enum MyEnum
{
ONE,
TWO,
THREE,
FOUR,
};
int main()
{
for (MyEnum v = MyEnum::ONE; v <= MyEnum::FOUR; ++(int&)v)
{
std::cout<<v<<std::endl;
}
return 0;
}
0
1
2
3
枚举就不行。也许枚举不是最适合您的情况。
一个常见的约定是将最后一个枚举值命名为MAX,并使用它来控制一个int类型的循环。
其他答案中没有涉及到的东西=如果你使用强类型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)
}
典型的方式如下:
enum Foo {
One,
Two,
Three,
Last
};
for ( int fooInt = One; fooInt != Last; fooInt++ )
{
Foo foo = static_cast<Foo>(fooInt);
// ...
}
请注意,枚举Last将被迭代跳过。利用这个“假的”Last enum,你不必每次想要添加一个新enum时都将for循环中的终止条件更新为最后一个“真实的”enum。 如果你以后想添加更多的枚举,只要在Last之前添加它们。本例中的循环仍然有效。
当然,如果指定了enum值,则会中断:
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值。
这里有一些非常易读且易于理解的方法,适用于弱类型的C和c++常规枚举,以及强类型的c++枚举类。
我建议使用-Wall -Wextra -Werror编译下面所有的例子。这为您提供了额外的安全性,如果您忘记在开关情况下覆盖任何枚举值,编译器将抛出编译时错误!这迫使您保持枚举定义和开关用例同步,这是代码的额外安全措施。只要你:
覆盖开关案例中的所有枚举值,和 没有默认开关大小写。 使用-Wall -Wextra -Werror旗帜构建。
我建议您遵循所有这3点,因为这是一个很好的实践,可以创建更好的代码。
1. 对于标准的弱类型C或c++ enum:
C定义(这也适用于c++):
typedef enum my_error_type_e
{
MY_ERROR_TYPE_SOMETHING_1 = 0,
MY_ERROR_TYPE_SOMETHING_2,
MY_ERROR_TYPE_SOMETHING_3,
MY_ERROR_TYPE_SOMETHING_4,
MY_ERROR_TYPE_SOMETHING_5,
/// Not a valid value; this is the number of members in this enum
MY_ERROR_TYPE_count,
// helpers for iterating over the enum
MY_ERROR_TYPE_begin = 0,
MY_ERROR_TYPE_end = MY_ERROR_TYPE_count,
} my_error_type_t;
c++定义:
enum my_error_type_t
{
MY_ERROR_TYPE_SOMETHING_1 = 0,
MY_ERROR_TYPE_SOMETHING_2,
MY_ERROR_TYPE_SOMETHING_3,
MY_ERROR_TYPE_SOMETHING_4,
MY_ERROR_TYPE_SOMETHING_5,
/// Not a valid value; this is the number of members in this enum
MY_ERROR_TYPE_count,
// helpers for iterating over the enum
MY_ERROR_TYPE_begin = 0,
MY_ERROR_TYPE_end = MY_ERROR_TYPE_count,
};
C或c++对弱类型枚举的迭代:
注意:通过my_error_type++递增枚举是不允许的——甚至在c风格的enum上也不允许,所以我们必须这样做:my_error_type = (my_error_type_t)(my_error_type + 1)。注意,my_error_type+ 1是允许的,然而,因为这个弱enum在这里会自动隐式强制转换为int类型,这样就可以不手动强制转换为int类型:my_error_type = (my_error_type_t)((int)my_error_type + 1)。
for (my_error_type_t my_error_type = MY_ERROR_TYPE_begin;
my_error_type < MY_ERROR_TYPE_end;
my_error_type = (my_error_type_t)(my_error_type + 1))
{
switch (my_error_type)
{
case MY_ERROR_TYPE_SOMETHING_1:
break;
case MY_ERROR_TYPE_SOMETHING_2:
break;
case MY_ERROR_TYPE_SOMETHING_3:
break;
case MY_ERROR_TYPE_SOMETHING_4:
break;
case MY_ERROR_TYPE_SOMETHING_5:
break;
case MY_ERROR_TYPE_count:
// This case will never be reached.
break;
}
}
2. 对于限定作用域的强类型c++枚举类:
c++定义:
enum class my_error_type_t
{
SOMETHING_1 = 0,
SOMETHING_2,
SOMETHING_3,
SOMETHING_4,
SOMETHING_5,
/// Not a valid value; this is the number of members in this enum
count,
// helpers for iterating over the enum
begin = 0,
end = count,
};
这个强类型枚举的c++迭代:
注意,强制递增枚举类变量需要额外的(size_t)强制转换(或者(int)也可以接受)!这里我还选择使用c++风格的static_cast<my_error_type_t>强制转换,但是C风格的(my_error_type_t)强制转换(如上所示)也可以。
for (my_error_type_t my_error_type = my_error_type_t::begin;
my_error_type < my_error_type_t::end;
my_error_type = static_cast<my_error_type_t>((size_t)my_error_type + 1))
{
switch (my_error_type)
{
case my_error_type_t::SOMETHING_1:
break;
case my_error_type_t::SOMETHING_2:
break;
case my_error_type_t::SOMETHING_3:
break;
case my_error_type_t::SOMETHING_4:
break;
case my_error_type_t::SOMETHING_5:
break;
case my_error_type_t::count:
// This case will never be reached.
break;
}
}
Also notice the scoping. In the C++ strongly-typed enum class I used my_error_type_t:: to access each scoped enum class member. But, in the C-style weakly-typed regular enum, very similar scoping can be achieved, as I demonstrated, simply be prefixing each enum member name with MY_ERROR_TYPE_. So, the fact that the C++ strongly-typed enum class adds scoping doesn't really add much value--it's really just a personal preference in that regard. And the fact that the C++ strongly-typed enum class has extra type-safety also has pros and cons. It may help you in some cases but it definitely makes incrementing the enum and iterating over it a pain-in-the-butt, which, honestly, means it is doing its job. By making it harder to increment the scoped enum class variable as though it was an integer, the C++ strongly-typed enum class is doing exactly what it was designed to do. Whether or not you want that behavior is up to you. Personally, I frequently do not want that behavior, and so it is not uncommon for me to prefer to use C-style enums even in C++.
参见:
[我的回答]在c++11中有一种方法通过索引初始化一个向量吗? [我的问答]在c++中迭代枚举类的常用方法是什么? 我对c++中枚举类(强类型enum)和常规enum(弱类型enum)之间的一些差异的回答:如何自动将强类型enum转换为int? 我的一些关于-Wall -Wextra -Werror和其他构建选项的个人笔记,来自我的eRCaGuy_hello_world回购。