在C ++中,是否有可能枚举枚举(无论是运行时或编译时(优选的))和呼叫功能/生成每个迭代码?
示例使用案例:
enum abc
{
start
a,
b,
c,
end
}
for each (__enum__member__ in abc)
{
function_call(__enum__member__);
}
似是而非的重复:
- C ++:遍历枚举
- 枚举类似于C ++枚举阿达?
在C ++中,是否有可能枚举枚举(无论是运行时或编译时(优选的))和呼叫功能/生成每个迭代码?
示例使用案例:
enum abc
{
start
a,
b,
c,
end
}
for each (__enum__member__ in abc)
{
function_call(__enum__member__);
}
似是而非的重复:
要添加到@StackedCrooked的回答,您可以重载operator++
, operator--
和operator*
和具有迭代器一样的功能。
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;
}
让我们从一些测试<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 ++目前不提供枚举迭代。 尽管如此,有时需要出现此。 一个常见的解决方法是添加标记开头和结尾值。 例如:
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));
}
}
无论是可能没有一点体力劳动。 很多工作都可以通过宏来完成的,如果你肯钻研该区域。
扩展在什么康拉德说,在“每次迭代生成代码”的情况下,一个可能的成语是使用一个包含文件来表示枚举:
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的日子一招,叫“类型安全枚举设计模式”。 你可以做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
}
}
如果你需要一个特殊的初始值,可以使一个常数,并设置它在你的枚举。 我没有检查这个编译,但它应该是接近在那里:-)。 希望这可以帮助。
我觉得这个方法可能是你的使用情况的良好平衡。 如果你不需要为很多不同的枚举类型做到这一点,你不想来处理预处理东西使用它。 只要确保你发表评论,并可能增加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。
#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
见亚伯拉罕的通道1,Gurtovoy“C ++模板元编程”的一个很好的治疗这种技术的。 在做它在运行时提出的技术,这种方式的优点在于,当你优化的代码,它可以内联静态和大致相当于:
function_call(a);
function_call(b);
function_call(c);
内嵌FUNCTION_CALL从编译器甚至更多的帮助。
其他枚举迭代技术,同样的批评适用于此。 此方法仅如果枚举不断从通过最终由那些递增的作品。
爱模板化,但我会记下这对我今后的/其他人的使用情况,以便我们没有失去任何以上的。
枚举是便于在已知有序的方式比较东西的缘故。 它们通常用于硬编码到功能可读性起见针对整数值。 有点类似于预处理器定义,与他们没有用文字代替,但保留在运行时访问异常。
如果我们有一个枚举定义HTML错误代码,我们知道的是500S的错误代码是服务器错误,它可能是更好的阅读是这样的:
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* 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;
当然还有它甚至更好,以创建一个通用类,它负责处理迭代像上面的答案枚举。