我只是注意到不能在枚举上使用标准数学运算符,如++或+=。
那么,在c++枚举中遍历所有值的最佳方法是什么?
我只是注意到不能在枚举上使用标准数学运算符,如++或+=。
那么,在c++枚举中遍历所有值的最佳方法是什么?
当前回答
如果枚举以0开头,且增量始终为1。
enum enumType
{
A = 0,
B,
C,
enumTypeEnd
};
for(int i=0; i<enumTypeEnd; i++)
{
enumType eCurrent = (enumType) i;
}
如果没有,我想唯一的原因是创造一个像
vector<enumType> vEnums;
添加条目,并使用普通的迭代器....
其他回答
如果你知道枚举值是连续的,例如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);
}
}
它像预期的那样工作。
枚举就不行。也许枚举不是最适合您的情况。
一个常见的约定是将最后一个枚举值命名为MAX,并使用它来控制一个int类型的循环。
如果枚举以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)
这里有一些非常易读且易于理解的方法,适用于弱类型的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回购。