with values recorrer que enum clase c++ c++11 operator-overloading enum-class

c++ - values - que es un enum en java



Implementación de operadores para clase enum. (3)

Después de la discusión en cuestión Incremento y decremento de la "clase de enumeración" , me gustaría preguntar sobre la posible implementación de operadores aritméticos para enum class tipos de enum class .

Ejemplo de la pregunta original:

enum class Colors { Black, Blue, White, END_OF_LIST }; // Special behavior for ++Colors Colors& operator++( Colors &c ) { c = static_cast<Colors>( static_cast<int>(c) + 1 ); if ( c == Colors::END_OF_LIST ) c = Colors::Black; return c; }

¿Hay una manera de implementar operadores aritméticos sin convertir a un tipo con operadores ya definidos? No puedo pensar en ninguno, pero el casting me molesta. Los moldes suelen indicar que algo está mal y tiene que haber una muy buena razón para su uso. Espero que el lenguaje permita la implementación de un operador sin tener que forzar a un tipo específico.


La solución sin conversión es usar el interruptor. Sin embargo, puede generar un pseudo-switch usando plantillas. El principio es procesar recursivamente todos los valores de la enumeración utilizando una lista de plantillas (o un paquete de parámetros). Entonces, aquí hay 3 métodos que encontré.

Prueba de enumeración:

enum class Fruit { apple, banana, orange, pineapple, lemon };

El interruptor de vainilla (vivir aquí) :

/// Non-scalable way Fruit& operator++(Fruit& f) { switch(f) { case Fruit::apple: return f = Fruit::banana; case Fruit::banana: return f = Fruit::orange; case Fruit::orange: return f = Fruit::pineapple; case Fruit::pineapple: return f = Fruit::lemon; case Fruit::lemon: return f = Fruit::apple; } }

El método C ++ 03-ish (en vivo aquí) :

template<typename E, E v> struct EnumValue { static const E value = v; }; template<typename h, typename t> struct StaticList { typedef h head; typedef t tail; }; template<typename list, typename first> struct CyclicHead { typedef typename list::head item; }; template<typename first> struct CyclicHead<void,first> { typedef first item; }; template<typename E, typename list, typename first = typename list::head> struct Advance { typedef typename list::head lh; typedef typename list::tail lt; typedef typename CyclicHead<lt, first>::item next; static void advance(E& value) { if(value == lh::value) value = next::value; else Advance<E, typename list::tail, first>::advance(value); } }; template<typename E, typename f> struct Advance<E,void,f> { static void advance(E& value) { } }; /// Scalable way, C++03-ish typedef StaticList<EnumValue<Fruit,Fruit::apple>, StaticList<EnumValue<Fruit,Fruit::banana>, StaticList<EnumValue<Fruit,Fruit::orange>, StaticList<EnumValue<Fruit,Fruit::pineapple>, StaticList<EnumValue<Fruit,Fruit::lemon>, void > > > > > Fruit_values; Fruit& operator++(Fruit& f) { Advance<Fruit, Fruit_values>::advance(f); return f; }

El método C ++ 11-ish (en vivo aquí) :

template<typename E, E first, E head> void advanceEnum(E& v) { if(v == head) v = first; } template<typename E, E first, E head, E next, E... tail> void advanceEnum(E& v) { if(v == head) v = next; else advanceEnum<E,first,next,tail...>(v); } template<typename E, E first, E... values> struct EnumValues { static void advance(E& v) { advanceEnum<E, first, first, values...>(v); } }; /// Scalable way, C++11-ish typedef EnumValues<Fruit, Fruit::apple, Fruit::banana, Fruit::orange, Fruit::pineapple, Fruit::lemon > Fruit_values11; Fruit& operator++(Fruit& f) { Fruit_values11::advance(f); return f; }

(C ++ 11-ish versión antigua)

Es posible que pueda extender agregando un preprocesador para eliminar la necesidad de repetir la lista de valores.


Todos los operadores en C ++ en las enumeraciones se pueden escribir sin transmitir a un tipo subyacente, pero el resultado sería ridículamente detallado.

Como ejemplo:

size_t index( Colors c ) { switch(c) { case Colors::Black: return 0; case Colors::Blue: return 1; case Colors::White: return 2; } } Color indexd_color( size_t n ) { switch(n%3) { case 0: return Colors::Black; case 1: return Colors::Blue; case 2: return Colors::White; } } Colors increment( Colors c, size_t n = 1 ) { return indexed_color( index(c) + n ); } Colors decrement( Colors c, size_t n = 1 ) { return indexed_color( index(c)+3 - (n%3) ); } Colors& operator++( Colors& c ) { c = increment(c) return c; } Colors operator++( Colors& c, bool ) { Colors retval = c; c = increment(c) return retval; }

y un compilador inteligente podrá convertirlas en operaciones que estén directamente en el tipo integral de base.

Pero convertir a un tipo integral base en la interfaz de tu enum class no es malo. Y los operadores son parte de la interfaz para su enum class .

Si no te gusta ese bucle a través de size_t y lo consideras un reparto falso, puedes escribir:

Colors increment( Colors c ) { switch(c) { case Colors::Black: return Colors::Blue; case Colors::Blue: return Colors::White; case Colors::White: return Colors::Black; } }

y de manera similar para disminuir, e implementar incremento por by como bucles de increment repetido.


enum class Colors { Black, Blue, White }; Colors operator++(Colors& color) { color = (color == Colors::White) ? Colors::Black : Colors(int(color) + 1); return color; }

Compruebe en C ++ Shell