循环枚举值

时间:2010-03-19 18:38:38

标签: c++ enums loops

在枚举上索引循环是多么可怕 - 或者它是否完全可以接受?

我定义了一个枚举。文字的值是默认值。指定的值没有任何意义,没有任何意义,将来添加的任何文字的值也没有任何意义。它被定义为限制允许的值并使事情更容易遵循。因此,值始终从0开始并增加1.

我可以像这样设置一个循环:

enum MyEnum
{
    value1,
    value2,
    value3,
    maxValue
}

for(MyEnum i = value1; i < maxValue; i = static_cast<MyEnum>(i+1)){}

9 个答案:

答案 0 :(得分:9)

我刚才为这些案例编写了一个枚举迭代器

enum Foo {
 A, B, C, Last
};

typedef litb::enum_iterator<Foo, A, Last> FooIterator;

int main() {
  FooIterator b(A), e;
  std::cout << std::distance(b, e)  << " values:" << std::endl;
  std::copy(b, e, std::ostream_iterator<Foo>(std::cout, "\n"));

  while(b != e) doIt(*b++);
}

如果您有兴趣,这是代码。如果您愿意,可以通过提供+<[]和朋友将其扩展为随机访问迭代器。像std::distance这样的算法会感谢你为随后的随机访问迭代器提供O(1)时间复杂度。

#include <cassert>

namespace litb {

template<typename Enum, Enum Begin, Enum End>
struct enum_iterator 
  : std::iterator<std::bidirectional_iterator_tag, Enum> {
  enum_iterator():c(End) { }
  enum_iterator(Enum c):c(c) { }

  enum_iterator &operator=(Enum c) {
    this->assign(c);
    return *this;
  }

  enum_iterator &operator++() {
    this->inc();
    return *this;
  }

  enum_iterator operator++(int) {
    enum_iterator cpy(*this);
    this->inc();
    return cpy;
  }

  enum_iterator &operator--() {
    this->dec();
    return *this;
  }

  enum_iterator operator--(int) {
    enum_iterator cpy(*this);
    this->dec();
    return cpy;
  }

  Enum operator*() const {
    assert(c != End && "not dereferencable!");
    return c;
  }

  bool equals(enum_iterator other) const {
    return other.c == c;
  }

private:
  void assign(Enum c) {
    assert(c >= Begin && c <= End);
    this->c = c; 
  }

  void inc() {
    assert(c != End && "incrementing past end");
    c = static_cast<Enum>(c + 1);
  }

  void dec() {
    assert(c != Begin && "decrementing beyond begin");
    c = static_cast<Enum>(c - 1);
  }

private:
  Enum c;
};

template<typename Enum, Enum Begin, Enum End>
bool operator==(enum_iterator<Enum, Begin, End> e1, enum_iterator<Enum, Begin, End> e2) {
  return e1.equals(e2);
}

template<typename Enum, Enum Begin, Enum End>
bool operator!=(enum_iterator<Enum, Begin, End> e1, enum_iterator<Enum, Begin, End> e2) {
  return !(e1 == e2);
}

} // litb

答案 1 :(得分:4)

就我而言,这很好。我肯定有些纯粹的东西会在某处发生,但就语言规范而言,该代码将正常工作,所以如果它让你的生活更轻松,你应该随意去做。

答案 2 :(得分:2)

这样做可能会有所帮助。

enum MyEnum
{
    first,
    value1,
    value2,
    value3,
    last
}

答案 3 :(得分:0)

只要您没有专门设置枚举值的值,那就没问题。你的例子很好,但这可能很糟糕:

// this would be bad
enum MyEnum
{
    value1,
    value2 = 2,
    value3,
    maxValue
};

正如7.2 / 1中的标准所述:

  

枚举器定义   没有初始值设定项为枚举数提供通过增加前一个值获得的值   一位普查员。

答案 4 :(得分:0)

我不知道具体使用什么实际上有用,我认为是错误的代码。 如果在许多图书馆中看到这样的东西怎么办?

enum MyEnum
{
    value1,
    value2,
    value3,
    maxValue = 99999;
};

答案 5 :(得分:0)

  

因此值始终开始   在0并且增加1。

请记住,除非将其写入您工作的编码标准,否则维护程序员会在6个月内出现并更改枚举,如:

enum MyEnum 
{ 
    value1, 
    value2 = 5, 
    value3, 
    maxValue 
}

由于新的要求,您将解释为什么他们的合法更改破坏了申请。

要将值附加到名称,您可以使用地图:

typedef std::map<std::string,int> ValueMap;
ValueMap myMap;
myMap.insert(make_pair("value1", 0));
myMap.insert(make_pair("value2", 1));
myMap.insert(make_pair("value3", 2));

for( ValueMap::iterator iter = theMap.begin(); iter != theMap.end(); ++iter )
{
  func(iter->second);
}

如果名称无关紧要,可以使用C风格的数组:

int values[] = { 0, 1, 2 };
int valuesSize = sizeof(values) / sizeof(values[0]);

for (int i = 0; i < valuesSize; ++i)
{
  func(values[i]);
}

使用std :: vector的类似方法。

另外,如果你说的是真的,the values will always start at 0 and increase by 1那么我很困惑为什么这不起作用:

const int categoryStartValue = 0;
const int categoryEndValue = 4;

for (int i = categoryStartValue; i < categoryEndValue; ++i)
{
  func(i);
}

答案 6 :(得分:0)

这不容易......

我找到的唯一解决方案是实际在枚举上实现一个类,然后使用宏来定义枚举...

DEFINE_NEW_ENUM(MyEnum, (value1)(value2)(value3))

基本思想是将值存储在STL容器中,根据MyEnum符号静态存储在模板类中。通过这种方式,您可以获得完美定义的迭代,甚至可以获得无效状态(因为end())。

我使用了vector的{​​{1}},这样我就可以从日志中的漂亮打印和长时间的序列化中受益(并且因为它比一组关联容器更快,只需很少的数据集并且需要更少的数据存储器)。

我还没有找到更好的方法,C ++ 11最终是否会在枚举上进行迭代?

答案 7 :(得分:0)

我通常在需要迭代时在我的枚举中定义开始值和结束值,使用如下特殊语法(使用doxygen样式注释来澄清):

enum FooType {
    FT__BEGIN = 0,        ///< iteration sentinel
    FT_BAR = FT__BEGIN,   ///< yarr!
    FT_BAZ,               ///< later, when the buzz hits  
    FT_BEEP,              ///< makes things go beep
    FT_BOOP,              ///< makes things go boop
    FT__END              ///< iteration sentinel
}

FT_可以帮助命名空间(以避免枚举之间的冲突)和双下划线有助于区分实际值和迭代标记。

旁注:

写这篇文章我开始担心用户标识符不允许双下划线(它的实现保留,IIRC?),但我还没遇到问题(用这种方式编写代码5到6年后)。它可能没问题,因为我总是将所有类型都放在命名空间中,以避免污染和干扰全局命名空间。

答案 8 :(得分:-1)

您可以使用switch语句包装循环体以防止非增量值。它可能超级慢,如maxValue = 9999;情况,但它可能不是有史以来最糟糕的事情。我在代码库中一直看到这种风格

enum MyEnum  {
 value1,  
 value2,  
 value3,  
 maxValue  
}  
for(MyEnum i = value1; i < maxValue; i = static_cast<MyEnum>(i+1)){
     switch(i)
     {
       case value1:
       case value2:
       case value3:
                    //actual code
     }

}