我只是注意到不能在枚举上使用标准数学运算符,如++或+=。
那么,在c++枚举中遍历所有值的最佳方法是什么?
我只是注意到不能在枚举上使用标准数学运算符,如++或+=。
那么,在c++枚举中遍历所有值的最佳方法是什么?
当前回答
#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)
扩展@Eponymous的回答:它很棒,但没有提供通用语法。这是我想到的:
// Common/EnumTools.h
#pragma once
#include <array>
namespace Common {
// Here we forward-declare metafunction for mapping enums to their values.
// Since C++<23 doesn't have reflection, you have to populate it yourself :-(
// Usage: After declaring enum class E, add this overload in the namespace of E:
// inline constexpr auto allValuesArray(const E&, Commob::EnumAllValuesTag) { return std::array{E::foo, E::bar}; }
// Then `AllValues<NS::E>` will call `allValuesArray(NS::E{}, EnumAllValuesTag)` which will resolve
// by ADL.
// Just be sure to keep it sync'd with your enum!
// Here's what you want to use in, e.g., loops: "for (auto val : Common::AllValues<MyEnum>) {"
struct EnumAllValuesTag {}; // So your allValuesArray function is clearly associated with this header.
template <typename Enum>
static inline constexpr auto AllValues = allValuesArray(Enum{}, EnumAllValuesTag{});
// ^ Just "constexpr auto" or "constexpr std::array<Enum, allValuesArray(Enum{}, EnumAllValuesTag{}).size()>" didn't work on all compilers I'm using, but this did.
} // namespace Common
然后在你的命名空间:
#include "Common/EnumTools.h"
namespace MyNamespace {
enum class MyEnum {
foo,
bar = 4,
baz = 42,
};
// Making this not have to be in the `Common` namespace took some thinking,
// but is a critical feature since otherwise there's no hope in keeping it sync'd with the enum.
inline constexpr auto allValuesArray(const MyEnum&, Common::EnumAllValuesTag) {
return std::array{ MyEnum::foo, MyEnum::bar, MyEnum::baz };
}
} // namespace MyNamespace
然后在任何需要使用它的地方:
for (const auto& e : Common::AllValues<MyNamespace::MyEnum>) { ... }
所以即使你有typeded:
namespace YourNS {
using E = MyNamespace::MyEnum;
} // namespace YourNS
for (const auto& e : Common::AllValues<YourNS::E>) { ... }
我想不出比这更好的了,除了每个人都想要的实际语言功能。
未来工作:
您应该能够添加一个constexpr函数(以及一个元函数)来过滤Common::AllValues<E>,从而为枚举具有重复数值的情况提供一个Common::AllDistinctValues<E>,例如enum {foo = 0, bar = 0};。 我打赌有一种方法可以使用编译器的switch- coverage -all-enum-values来编写allValuesArray,这样如果枚举添加了一个值,它就会出错。
我经常这样做
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_None = 0,
E_First = 0x1,
E_CanFly = E_First,
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))
{}
在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。但我更喜欢枚举类,因为它们是强类型的,可以防止我们在编译时犯错误。
如果你知道枚举值是连续的,例如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);
}
}
它像预期的那样工作。