86 votes

Autoriser les classes basées sur une plage avec enum?

J'ai un récurrentes morceau de code où je boucle sur tous les membres de l' enum class.

L' for boucle que j'utilise actuellement a l'air très difficile par rapport à la nouvelle - range-based for.

Est-il possible de prendre avantage de la nouvelle C++11 caractéristiques de couper vers le bas sur le niveau de verbosité pour mon for boucle?

Code actuel que j'aimerais améliorer:

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

En d'autres termes, il serait bien si je pouvais faire quelque chose comme:

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

60voto

deft_code Points 19418

Personnellement, je n'aime pas trop surcharger l'opérateur ++ pour les enums. Augmenter une valeur enum n'a souvent aucun sens. Tout ce qui est vraiment recherché, c'est un moyen de se déplacer sur l'énumération.

Vous trouverez ci-dessous une classe générique Enum qui prend en charge l'itération. C'est fonctionnel mais incomplet. Une implémentation réelle ferait bien de restreindre l'accès au constructeur et d'ajouter tous les traits d'itérateur.

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

42voto

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

Ensuite:

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

Plusieurs fois, je l'utilise comme ceci, où je veux une valeur 'aucune':

 // Color of a piece on a chess board
enum class Color {
    white,
    black,
    none
};
const std::array<Color,3> colors = {white,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
    // ...
}
 

40voto

Mooing Duck Points 27497

Itérer des énumérations avec l'énumération elle-même en tant qu'itérateur est une mauvaise idée, et je recommande d'utiliser un itérateur réel comme dans la réponse de deft_code. Mais si c'est vraiment ce que vous voulez:

 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 First;}
COLOR end(COLOR r)   {return Last;}

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

Travailler ici: http://ideone.com/b7pORw

7voto

mark Points 2609

Vous pouvez probablement faire quelque chose d'intelligent avec boost :: mpl, une version brute pourrait ressembler à ceci:

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

4voto

jrok Points 30472

Voici un exemple testé (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;
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X