允许使用枚举类的基于范围的For?

kfm*_*e04 65 c++ enums for-loop c++11

我有一个经常出现的代码块,我循环遍历所有成员enum class.

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

有没有办法利用新的C++ 11功能来减少当前for循环的详细程度?

我希望改进的当前代码:

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;
}
Run Code Online (Sandbox Code Playgroud)

换句话说,如果我可以做以下事情会很好:

for( const auto& c : COLOR )
{
  // do work
}
Run Code Online (Sandbox Code Playgroud)

def*_*ode 50

我个人不喜欢为++运算符重载运算符.通常递增枚举值并不真正有意义.所有真正想要的是一种迭代枚举的方法.

下面是一个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;
   }
}
Run Code Online (Sandbox Code Playgroud)

  • [工作完成实现](http://stacked-crooked.com/view?id=bcee5da83cd5c0738a17962bc00ee82d),还要注意我将"Last"的值更改为与正常的迭代器范围更加一致. (8认同)

use*_*322 35

enum class Color {
    blue,
    red,
    green = 5,
    purple
};
const std::array<Color,4> all_colors = {Color::blue, Color::red, Color::green, Color::purple};
Run Code Online (Sandbox Code Playgroud)

然后:

for (Color c : all_colors) {
    //...
}
Run Code Online (Sandbox Code Playgroud)

很多时候我这样使用它,我想要一个'none'值:

// 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
    // ...
}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的答案!将物品放在两个地方的冗余很可惜,但它比其他解决方案更清洁; 这没有强制转换,正如您所提到的,值不必从零开始或连续.您还可以轻松指定值的子集,例如darkColors和lightColors.它依赖于具有相当少数值的枚举,但它们通常无论如何都会这样做. (8认同)
  • 为什么数组的大小指定为"3"?不应该是`2`?如果是这样,这是否说明违反DRY原则总会导致难以看到的错误? (2认同)
  • 如果有人增加了价值,那真的很糟糕。比您的数组没有涵盖所有值! (2认同)

Moo*_*uck 30

使用枚举本身作为迭代器迭代枚举是一个糟糕的想法,我建议在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;
}
Run Code Online (Sandbox Code Playgroud)

在这里工作: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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 需要一个`operator*`来使`COLOR`成为输入迭代器. (4认同)
  • @ kfmfe04我把它添加到了答案中. (3认同)
  • @MooingDuck ++不适用于强类型枚举,但kfmfe04在他的问题上有一个实现. (2认同)

mar*_*ark 6

您可能可以通过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;
}
Run Code Online (Sandbox Code Playgroud)


Cod*_*Dan 6

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

enum class Color {Red, Green, Blue};

for (const Color c : {Color::Red, Color::Green, Color::Blue})
{
}
Run Code Online (Sandbox Code Playgroud)

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