枚举C ++中的枚举
在C ++中,是否可以枚举一个枚举(运行时或编译时(首选))和调用函数/为每个迭代生成代码?
示例用例:
enum abc { start a, b, c, end } for each (__enum__member__ in abc) { function_call(__enum__member__); }
似是而非的重复:
- C ++:遍历一个枚举
- Enum在C + +像枚举在Ada?
要添加到@StackedCrooked答案,您可以重载operator++
, operator--
和operator*
并具有迭代器的function。
enum Color { Color_Begin, Color_Red = Color_Begin, Color_Orange, Color_Yellow, Color_Green, Color_Blue, Color_Indigo, Color_Violet, Color_End }; namespace std { template<> struct iterator_traits<Color> { typedef Color value_type; typedef int difference_type; typedef Color *pointer; typedef Color &reference; typedef std::bidirectional_iterator_tag iterator_category; }; } Color &operator++(Color &c) { assert(c != Color_End); c = static_cast<Color>(c + 1); return c; } Color operator++(Color &c, int) { assert(c != Color_End); ++c; return static_cast<Color>(c - 1); } Color &operator--(Color &c) { assert(c != Color_Begin); return c = static_cast<Color>(c - 1); } Color operator--(Color &c, int) { assert(c != Color_Begin); --c; return static_cast<Color>(c + 1); } Color operator*(Color c) { assert(c != Color_End); return c; }
我们来testing一下<algorithm>
模板
void print(Color c) { std::cout << c << std::endl; } int main() { std::for_each(Color_Begin, Color_End, &print); }
现在, Color
是一个不变的双向迭代器。 这里是一个可重复使用的类,我在上面手动进行编码。 我注意到它可以为更多的枚举工作,所以重复相同的代码是非常繁琐的
// Code for testing enum_iterator // -------------------------------- namespace color_test { enum Color { Color_Begin, Color_Red = Color_Begin, Color_Orange, Color_Yellow, Color_Green, Color_Blue, Color_Indigo, Color_Violet, Color_End }; Color begin(enum_identity<Color>) { return Color_Begin; } Color end(enum_identity<Color>) { return Color_End; } } void print(color_test::Color c) { std::cout << c << std::endl; } int main() { enum_iterator<color_test::Color> b = color_test::Color_Begin, e; while(b != e) print(*b++); }
执行如下。
template<typename T> struct enum_identity { typedef T type; }; namespace details { void begin(); void end(); } template<typename Enum> struct enum_iterator : std::iterator<std::bidirectional_iterator_tag, Enum> { enum_iterator():c(end()) { } enum_iterator(Enum c):c(c) { assert(c >= begin() && c <= end()); } enum_iterator &operator=(Enum c) { assert(c >= begin() && c <= end()); this->c = c; return *this; } static Enum begin() { using details::begin; // re-enable ADL return begin(enum_identity<Enum>()); } static Enum end() { using details::end; // re-enable ADL return end(enum_identity<Enum>()); } enum_iterator &operator++() { assert(c != end() && "incrementing past end?"); c = static_cast<Enum>(c + 1); return *this; } enum_iterator operator++(int) { assert(c != end() && "incrementing past end?"); enum_iterator cpy(*this); ++*this; return cpy; } enum_iterator &operator--() { assert(c != begin() && "decrementing beyond begin?"); c = static_cast<Enum>(c - 1); return *this; } enum_iterator operator--(int) { assert(c != begin() && "decrementing beyond begin?"); enum_iterator cpy(*this); --*this; return cpy; } Enum operator*() { assert(c != end() && "cannot dereference end iterator"); return c; } Enum get_enum() const { return c; } private: Enum c; }; template<typename Enum> bool operator==(enum_iterator<Enum> e1, enum_iterator<Enum> e2) { return e1.get_enum() == e2.get_enum(); } template<typename Enum> bool operator!=(enum_iterator<Enum> e1, enum_iterator<Enum> e2) { return !(e1 == e2); }
C ++目前不提供枚举器迭代。 尽pipe如此,有时需要这样做。 常见的解决方法是添加标记开始和结束的值。 例如:
enum Color { Color_Begin, Color_Red = Color_Begin, Color_Orange, Color_Yellow, Color_Green, Color_Blue, Color_Indigo, Color_Violet, Color_End }; void foo(Color c) { } void iterateColors() { for (size_t colorIdx = Color_Begin; colorIdx != Color_End; ++colorIdx) { foo(static_cast<Color>(colorIdx)); } }
没有一点体力劳动也是不可能的。 如果你愿意深入研究这个领域,很多工作都可以通过macros来完成。
根据康拉德的说法,在“为每次迭代生成代码”的情况下,一种可能的习惯用法是使用包含的文件来表示枚举:
mystuff.h:
#ifndef LAST_ENUM_ELEMENT #define LAST_ENUM_ELEMENT(ARG) ENUM_ELEMENT(ARG) #endif ENUM_ELEMENT(foo) ENUM_ELEMENT(bar) LAST_ENUM_ELEMENT(baz) // not essential, but most likely every "caller" should do it anyway... #undef LAST_ENUM_ELEMENT #undef ENUM_ELEMENT
enum.h:
// include guard goes here (but mystuff.h doesn't have one) enum element { #define ENUM_ELEMENT(ARG) ARG, #define LAST_ENUM_ELEMENT(ARG) ARG #include "mystuff.h" }
main.cpp中:
#include "enum.h" #define ENUM_ELEMENT(ARG) void do_##ARG(); #include "mystuff.h" element value = getValue(); switch(value) { #define ENUM_ELEMENT(ARG) case ARG: do_##ARG(); break; #include "mystuff.h" default: std::terminate(); }
所以,要添加一个新的元素“qux”,你可以将它添加到mystuff.h中,并写入do_qux
函数。 您不必触摸调度代码。
当然,如果你的枚举中的值需要是特定的非连续整数,那么你最终将分别维护枚举定义和ENUM_ELEMENT(foo)
…列表,这很杂乱。
没有
但是,您可以定义自己的类,通过迭代实现枚举类特性。 您可能还记得1.5 Java前的一个技巧,被称为“types安全枚举devise模式”。 你可以做C ++的等价物。
这对我来说似乎很不舒服,但可能适合你的目的:
enum Blah { FOO, BAR, NUM_BLAHS }; // later on for (int i = 0; i < NUM_BLAHS; ++i) { switch (i) { case FOO: // foo stuff break; case BAR: // bar stuff break; default: // you're missing a case statement } }
如果你需要一个特殊的开始值,你可以使这个常量,并将其设置在您的枚举。 我没有检查是否编译,但应该接近于:-)。 希望这可以帮助。
我认为这种方法可能是一个很好的平衡你的用例。 如果你不需要为一堆不同的枚举types使用它,你不想处理预处理器的东西。 只要确保你的评论,并可能添加一个TODO以后更改为更好的:-)。
我通常这样做:
enum abc { abc_begin, a = abc_begin, b, c, abc_end }; void foo() { for( auto&& r : range(abc_begin,abc_end) ) { cout << r; } }
range
是完全通用的,定义如下:
template <typename T> class Range { public: Range( const T& beg, const T& end ) : b(beg), e(end) {} struct iterator { T val; T operator*() { return val; } iterator& operator++() { val = (T)( 1+val ); return *this; } bool operator!=(const iterator& i2) { return val != i2.val; } }; iterator begin() const { return{b}; } iterator end() const { return{e}; } private: const T& b; const T& e; }; template <typename T> Range<T> range( const T& beg, const T& end ) { return Range<T>(beg,end); }
您可以使用TMP静态执行一些build议的运行时技术。
#include <iostream> enum abc { a, b, c, end }; void function_call(abc val) { std::cout << val << std::endl; } template<abc val> struct iterator_t { static void run() { function_call(val); iterator_t<static_cast<abc>(val + 1)>::run(); } }; template<> struct iterator_t<end> { static void run() { } }; int main() { iterator_t<a>::run(); return 0; }
这个程序的输出是:
0 1 2
参见Abrahams的第1章,Gurtovoy的“C ++模板元编程”,对这种技术进行了很好的处理。 这样做比build议的运行时技术的优点是,当你优化这个代码时,它可以内联静态,大致相当于:
function_call(a); function_call(b); function_call(c);
内联function_call获得编译器的更多帮助。
其他枚举迭代技术的相同批评在这里适用。 这种技术只有在你的枚举从一个直到一个连续增加时才有效。
爱模板,但我会记下这个为我的未来/其他人的使用,所以我们不会失去任何上述。
为了以已知的有序方式比较事物,枚举是方便的。 它们通常用硬编码到函数中,以便对整数值进行读取。 有点类似于预处理器的定义,除了它们不是用文字replace,而是在运行时保存和访问的。
如果我们有一个定义html错误代码的枚举,并且我们知道500年代的错误代码是服务器错误,那么读取如下所示的内容可能会更好:
enum HtmlCodes {CONTINUE_CODE=100,CLIENT_ERROR=400,SERVER_ERROR=500,NON_STANDARD=600}; if(errorCode >= SERVER_ERROR && errorCode < NON_STANDARD)
比
if(errorCode >= 500 && errorCode < 600)
关键部分是这个,它们和数组类似! 但是用来 投射 整数值 。
简短的例子:
enum Suit {Diamonds, Hearts, Clubs, Spades}; //does something with values in the enum past "Hearts" in this case for(int i=0;i<4;i++){ //Could also use i or Hearts, because the enum will turns these both back into an int if( (Suit)(i) > 1 ) { //Whatever we'd like to do with (Suit)(i) } }
通常枚举也可以和char *数组或string数组一起使用,这样就可以用相关的值打印一些消息。 通常他们只是在枚举中具有相同的一组值,如下所示:
char* Suits[4] = {"Diamonds", "Hearts", "Clubs", "Spades"}; //Getting a little redundant cout << Suits[Clubs] << endl; //We might want to add this to the above //cout << Suits[(Suit)(i)] << endl;
当然,创build一个generics类来处理枚举types的迭代就更好了。