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