C++ 允许基于范围的 For 枚举类?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8498300/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 18:41:03  来源:igfitidea点击:

Allow for Range-Based For with enum classes?

c++for-loopc++11enums

提问by kfmfe04

I have a recurrent chunk of code where I loop over all the members of an enum class.

我有一个循环代码块,我循环遍历enum class.

The forloop that I currently use looks very unwieldly compared to the new range-based for.

for循环,我目前使用的看起来很笨拙相比,新的range-based for

Is there any way to take advantage of new C++11 features to cut down on the verbosity for my current forloop?

有什么方法可以利用新的 C++11 特性来减少当前for循环的冗长?

Current Code that I would like to improve:

我想改进的当前代码:

enum class COLOR
{
    Blue,
    Red,
    Green,
    Purple,
    First=Blue,
    Last=Purple
};

inline COLOR operator++( COLOR& x ) { return x = (COLOR)(((int)(x) + 1)); }

int main(int argc, char** argv)
{
  // any way to improve the next line with range-based for?
  for( COLOR c=COLOR::First; c!=COLOR::Last; ++c )
  {
    // do work
  }
  return 0;
}

In other words, it would be nice if I could do something like:

换句话说,如果我能做这样的事情就好了:

for( const auto& c : COLOR )
{
  // do work
}

采纳答案by Mooing Duck

Iterating enumerations with the enumeration itself as an iterator is a poor idea, and I recommend using an actual iterator as in deft_code's answer. But if this is really what you want:

使用枚举本身作为迭代器来迭代枚举是一个糟糕的主意,我建议使用实际的迭代器,如 deft_code 的答案。但如果这真的是你想要的:

COLOR operator++(COLOR& x) {
    return x = (COLOR)(std::underlying_type<COLOR>::type(x) + 1); 
}

COLOR operator*(COLOR c) {
    return c;
}

COLOR begin(COLOR r) {
    return COLOR::First;
}

COLOR end(COLOR r) {
    COLOR l=COLOR::Last;
    return ++l;
}

int main() { 
    //note the parenthesis after COLOR to make an instance
    for(const auto& c : COLOR()) {
        //do work
    }
    return 0;
}

Working here: http://ideone.com/cyTGD8

在这里工作:http: //ideone.com/cyTGD8



在迭代器方面,最简单的方法很简单:

extern const COLOR COLORS[(int)COLOR::Last+1];
const COLOR COLORS[] = {COLOR::Blue, COLOR::Red, COLOR::Green, COLOR::Purple};

int main() { 
    for(const auto& c : COLORS) {
        //do work
    }
    return 0;
}

As seen here: http://ideone.com/9XadVt

如下所示:http: //ideone.com/9XadVt

(The separate declaration and defintinion of the array makes it a compiler error if the number of colors doesn't match the number of elements in the array. Excellent easy safety check.)

(如果颜色数量与数组中的元素数量不匹配,则数组的单独声明和定义会导致编译器错误。非常容易安全检查。)

回答by deft_code

I personally don't like overloading the ++operator for enums. Often incrementingan enum value doesn't really make sense. All that is really wanted is a way to iterator over the enum.

我个人不喜欢++为枚举重载运算符。通常增加枚举值并没有真正意义。真正需要的是一种遍历枚举的方法。

Below is an generic Enumclass that supports iteration. It's functional but incomplete. A real implementation would do well to restrict access to the constructor and add all the iterator traits.

下面是一个Enum支持迭代的泛型类。它是功能性的,但不完整。一个真正的实现可以很好地限制对构造函数的访问并添加所有迭代器特征。

#include <iostream>

template< typename T >
class Enum
{
public:
   class Iterator
   {
   public:
      Iterator( int value ) :
         m_value( value )
      { }

      T operator*( void ) const
      {
         return (T)m_value;
      }

      void operator++( void )
      {
         ++m_value;
      }

      bool operator!=( Iterator rhs )
      {
         return m_value != rhs.m_value;
      }

   private:
      int m_value;
   };

};

template< typename T >
typename Enum<T>::Iterator begin( Enum<T> )
{
   return typename Enum<T>::Iterator( (int)T::First );
}

template< typename T >
typename Enum<T>::Iterator end( Enum<T> )
{
   return typename Enum<T>::Iterator( ((int)T::Last) + 1 );
}

enum class Color
{
   Red,
   Green,
   Blue,
   First = Red,
   Last = Blue
};

int main()
{
   for( auto e: Enum<Color>() )
   {
      std::cout << ((int)e) << std::endl;
   }
}

回答by user1594322

enum class Color {
    blue,
    red,
    green = 5,
    purple
};
const std::array<Color,4> all_colors = {Color::blue, Color::red, Color::green, Color::purple};

Then:

然后:

for (Color c : all_colors) {
    //...
}

Many times I use it like this, where I want a 'none' value:

很多时候我这样使用它,我想要一个“无”值:

// Color of a piece on a chess board
enum class Color {
    white,
    black,
    none
};
const std::array<Color,3> colors = {Color::white, Color::black};

template <typename CONTAINER>
bool has_item (CONTAINER const & c, typename CONTAINER::const_reference v) {
    return std::find(c.begin(), c.end(), v) != c.end();
}

bool is_valid (Color c) {
    return has_item(colors, c) || c == Color::none;
}

bool do_it (Color c) {
    assert(has_item(colors, c)); // here I want a real color, not none
    // ...
}

bool stop_it (Color c) {
    assert(is_valid(c));         // but here I just want something valid
    // ...
}

回答by mark

You could probably do something clever with boost::mpl, a rough version might look like:

您可能可以使用 boost::mpl 做一些聪明的事情,粗略的版本可能如下所示:

#include <typeinfo>

// ---------------------------------------------------------------------------|
// Boost MPL
// ---------------------------------------------------------------------------|
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/iterator_range.hpp>
#include <boost/mpl/range_c.hpp>

namespace mpl = boost::mpl;

using namespace std;

enum class COLOR 
{ 
   Blue,
   Red,
   Green,
   Purple,
   Last
};

struct enumValPrinter
{
    template< typename T >
    void operator() (const T&)
    {
        cout << "enumValPrinter with: " << typeid( T ).name() << " : " 
             << T::value << "\n";
    }
};

int main(int, char**)
{
    typedef mpl::range_c< int, static_cast<int>( COLOR::Blue ), 
                            static_cast<int>( COLOR::Last ) > Colors;
    mpl::for_each< Colors >( enumValPrinter() );
    return 0;
}

回答by jrok

Here's a tested example (GCC 4.6.1):

这是一个经过测试的示例(GCC 4.6.1):

enum class COLOR
{
    Blue,
    Red,
    Green,
    Purple,
    First=Blue,
    Last=Purple
};

COLOR operator++( COLOR& x ) { return x = (COLOR)(((int)(x) + 1)); }

COLOR operator*(COLOR c) {return c;}

COLOR begin(COLOR r) {return COLOR::First;}
// end iterator needs to return one past the end!
COLOR end(COLOR r)   {return COLOR(int(COLOR::Last) + 1);}


int main()
{
    for (const auto& color : COLOR()) std::cout << int(color); //0123
    return 0;
}

回答by Raven Black

If you're a terrible person you can get this behavior with the preprocessor, something like:

如果你是一个糟糕的人,你可以通过预处理器得到这种行为,比如:

#include <vector>
#include <cstdio>

#define ENUM_NAME COLOR
#define ENUM_VALUES \
    ENUM_VALUE(Blue) \
    ENUM_VALUE(Red) \
    ENUM_VALUE(Green) \
    ENUM_VALUE(Purple)

// This block would be a #include "make_iterable_enum.h"
#define ENUM_VALUE(v) v,
enum class ENUM_NAME {ENUM_VALUES};
#undef ENUM_VALUE
#define ENUM_VALUE(v) ENUM_NAME::v,
#define VECTOR_NAME(v) values_ ## v
#define EXPAND_TO_VECTOR_NAME(v) VECTOR_NAME(v)
const std::vector<ENUM_NAME> EXPAND_TO_VECTOR_NAME(ENUM_NAME){ENUM_VALUES};
#undef ENUM_VALUE
#undef ENUM_NAME
#undef ENUM_VALUES
#undef VECTOR_NAME
#undef EXPAND_TO_VECTOR_NAME
// end #included block

int main() {
    for (auto v : COLOR_values) {
        printf("%d\n", (int)v);
    }
}

With minor modifications this could also support eg. ENUM_SETVALUE(Blue, 4) and making a const map from eg. COLOR::Blue to "Blue". And vice-versa.

稍加修改,这也可以支持例如。ENUM_SETVALUE(Blue, 4) 并从例如制作一个常量映射。颜色::蓝色到“蓝色”。反之亦然。

I wish the standard had just built these features in as options to enum class. None of the workarounds are good.

我希望标准刚刚构建了这些功能作为枚举类的选项。没有一种解决方法是好的。

回答by Coder_Dan

I'm sure that you can iterate over the members of a C++ initializer_list, so I reckon I've done this in the past:

我确定您可以遍历 C++ initializer_list 的成员,所以我认为我过去已经这样做了:

enum class Color {Red, Green, Blue};

for (const Color c : {Color::Red, Color::Green, Color::Blue})
{
}

Whether there are issues with this, I don't know, but I thought I'd suggest it as it is concise, but not ideal if there are a lot of Colors.

这是否有问题,我不知道,但我认为我会建议它,因为它简洁,但如果有很多颜色就不理想了。

回答by emsr

I like the idea a lot and have often wished for it.

我非常喜欢这个想法并且经常希望它。

The problem I see is what happens when there is a repeated numeric value for an enum item. All the implementations I see above require casts to integral type and ++. Ultimately, I think language support might be required to truly iterate over each item in all cases. It would remove the need to have First, Last or Begin, End although I don't object to this too much. It's like looking for begin() end() for containers.

我看到的问题是当枚举项有重复的数值时会发生什么。我在上面看到的所有实现都需要转换为整数类型和 ++。最终,我认为可能需要语言支持才能在所有情况下真正迭代每个项目。这将消除对 First、Last 或 Begin、End 的需要,尽管我并不太反对这一点。这就像为容器寻找 begin() end() 。

enum class COLOR 
{
   Blue,
   Red,
   Green,
   Mauve = 0,
   Purple,
   Last
};

The numbering starts over at Mauve.

编号从 Mauve 开始。

回答by rm1948

Whether or not you approve of incrementing enums there are times when it is useful. So here's a simple way of doing so:

无论您是否同意增加枚举,有时它都是有用的。所以这里有一个简单的方法:

enum class COLOR
{
    Blue,
    Red,
    Green,
    Purple,
    First=Blue,
    Last=Purple
};

COLOR c;

++( *reinterpret_cast<int*>( &c));

There is no overhead since the compiler will take care of the casting and de-referencing. Add range checking or other capabilities as necessary.

没有开销,因为编译器会负责转换和取消引用。根据需要添加范围检查或其他功能。

回答by matthiascy

As a modification of @deft_code's answer, you don't need to define the Firstand the Lastin your enum class, just add two parameters for the templated Enumclass.

作为@deft_code 答案的修改,您不需要在您FirstLast中定义 the和 the enum class,只需为模板化Enum类添加两个参数即可。

template< typename T, T _Fist, T _Last >
class Enum
{
public:
   class Iterator
   {
   public:
      Iterator( int value ) :
         m_value( value )
      { }

      T operator*( void ) const
      {
         return (T)m_value;
      }

      void operator++( void )
      {
         ++m_value;
      }

      bool operator!=( Iterator rhs )
      {
         return m_value != rhs.m_value;
      }

   private:
      int m_value;
   };

};

template< typename T, T _Fist, T _Last >
typename Enum<T, _First, _Last >::Iterator begin( Enum<T, _First, _Last> )
{
   return typename Enum<T, _First, _Last>::Iterator( (int)_First );
}

template< typename T, T _Fist, T _Last >
typename Enum<T, _First, _Last>::Iterator end( Enum<T, _First, _Last> )
{
   return typename Enum<T, _First, _Last>::Iterator( ((int)_Last) + 1 );
}