循环枚举值

发布于 2024-08-26 01:17:11 字数 336 浏览 25 评论 0原文

在枚举上索引循环有多糟糕——或者完全可以接受吗?

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

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

enum MyEnum
{
    value1,
    value2,
    value3,
    maxValue
}

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

How awful is it - or is it perfectly acceptable - to index a loop on an enumeration?

I have an enumeration defined. The values of the literals are default values. The assigned values do not have any significance, will not have any significance, and the values of any literals added in the future will also not have any significance. It's just defined to limit the allowed values and to make things easier to follow. Therefore the values will always start at 0 and increase by 1.

Can I set up a loop like so:

enum MyEnum
{
    value1,
    value2,
    value3,
    maxValue
}

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

倾城泪 2024-09-02 01:17:11

我不久前为这些情况编写了一个枚举迭代器

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

I wrote an enum iterator a while ago for these cases

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++);
}

If you are interested, here is the code. If you like, you can extend it to be a random access iterator by providing +, <, [] and friends. Algorithms like std::distance will thank you by providing O(1) time complexity for the then random-access iterator.

#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
愿得七秒忆 2024-09-02 01:17:11

就我而言,这很好。我确信某些地方的纯粹主义者会感到害怕,但就语言规范而言,该代码将正常工作,因此如果它能让您的生活更轻松,您应该随意使用它。

As far as I'm concerned, that's just fine. I'm sure some purist out there somewhere will freak out, but as far as the language spec is concerned, that code will work correctly, so you should feel free to go for it if it makes your life easier.

甜`诱少女 2024-09-02 01:17:11

这样做可能会有所帮助。

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

It might be helpful to do this.

enum MyEnum
{
    first,
    value1,
    value2,
    value3,
    last
}
憧憬巴黎街头的黎明 2024-09-02 01:17:11

只要您不专门设置枚举值的值就可以。你的例子很好,但这可能很糟糕:

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

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

枚举器定义
没有初始化器为枚举器提供通过增加前一个值而获得的值
枚举器加一。

That is fine as long as you don't specifically set the value of an enum value. Your example is fine, but this could be bad:

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

As the standard states in 7.2/1:

An enumerator-definition
without an initializer gives the enumerator the value obtained by increasing the value of the previous
enumerator by one.

放肆 2024-09-02 01:17:11

我不知道它有任何实际有用的具体用途,我认为这是糟糕的代码。
如果有人像许多图书馆那样放置这样的东西怎么办?

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

I don't know any specific use of this that is actually useful and I think is bad code.
What if one put things like this as seen in many libraries ?

enum MyEnum
{
    value1,
    value2,
    value3,
    maxValue = 99999;
};
非要怀念 2024-09-02 01:17:11

这并不容易...

我发现的唯一解决方案是通过枚举实际实现一个类,然后使用宏来定义枚举...

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

基本思想是将值存储在 STL 容器中,静态存储在取决于 MyEnum 符号的模板类。这样你就得到了一个完美定义的迭代,甚至你甚至得到了一个无效状态(因为 end())。

我使用了 pair 的排序向量,这样我就可以从日志中漂亮的打印和持久的序列化中受益(而且因为它比用于少量数据集的关联容器,占用更少的内存)。

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

It's not easy...

The only solution I ever found was to actually implement a class over the enum, and then use a macro to define the enum...

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

The basic idea is to store the values in a STL container, stored statically in a template class depending on the MyEnum symbol. This way you get a perfectly well defined iteration, and you even get an invalid state for naught (because of the end()).

I used a sorted vector of pair<Enum,std::string> so that I could benefit from pretty printing in the logs and long lasting serialization (and because it's faster than an associative container for little sets of data and takes less memory).

I didn't found a better way yet, does C++11 finally get iteration on enums or not ?

只是在用心讲痛 2024-09-02 01:17:11

因此这些值将始终开始
为 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 的类似方法。

另外,如果你说的是真的,并且值总是从 0 开始并增加 1 那么我很困惑为什么这不起作用:

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

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

Therefore the values will always start
at 0 and increase by 1.

Keep in mind that unless this is written into the coding standard where you work, when a maintenance programmer comes along in 6 months and makes a change to an enumeration like:

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

because of a new requirement, you'll get to explain why their legitimate change broke the application.

To attach values to names you could use a map:

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);
}

If the names don't matter you could use a C-style array:

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

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

Or a comparable method using a std::vector.

Also, if what you say is true, and the values will always start at 0 and increase by 1 then I'm confused as to why this wouldn't work:

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

for (int i = categoryStartValue; i < categoryEndValue; ++i)
{
  func(i);
}
彻夜缠绵 2024-09-02 01:17:11

当我需要迭代时,我通常在枚举中定义开始和结束值,使用如下所示的特殊语法(用 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 年的编码以这种风格)。这可能没问题,因为我总是将所有类型放在命名空间中,以避免污染和干扰全局命名空间。

I usually define begin and end values in my enums when I need iteration, with a special syntax like the following (with doxygen style comments to clarify):

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_ is there to help with namespacing (to avoid clashes between enums) and the double underscores help distinguish between real values and iteration sentinels.

Sidenote:

Writing this I start worry about the double underscore not being allowed for user identifier (it's implementation reserved, IIRC?), but I'm yet to hit a problem (after 5-6 years of coding in this style). It might be ok since I always put all my types in a namespace to avoid polluting and interfering with the global namespace.

ゞ记忆︶ㄣ 2024-09-02 01:17:11

您可以使用 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
     }

}  

You could wrap the loop body with a switch statement to protect against non-incremental values. It's potentially super slow, as in the maxValue = 9999; case, but it might not be the worst thing ever. I see this style here all the time in our code bases

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
     }

}  
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文