2010-08-13 16 views
11

Tengo un tipo enumerado StackID, y estoy usando la enumeración para referirme a un índice de un vector particular y hace que mi código sea más fácil de leer. Sin embargo, ahora necesito crear una variable llamada nextAvail de tipo StackID. (en realidad se refiere a un stackID particular). Traté de incrementarlo pero en C++, el siguiente es ilegal:¿Por qué no puedo incrementar una variable de un tipo enumerado?

nextAvail++; 

¿Qué tipo de sentido para mí ... porque no hay comprobación de límites.

Probablemente esté pasando por alto algo obvio, pero ¿cuál es un buen sustituto?


También quiero hacer un enlace a this pregunta.

Respuesta

16

Probablemente estoy pasando por alto algo obvio, pero ¿cuál es un buen sustituto?

Sobrecarga operator++:

// Beware, brain-compiled code ahead! 
StackID& operator++(StackID& stackID) 
{ 
#if MY_ENUMS_ARE_CONTIGUOUS && I_DO_NOT_WORRY_ABOUT_OVERFLOW 
    return stackID = static_cast<StackID>(++static_cast<int>(stackID)); 
#else 
    switch(stackID) { 
    case value1 : return stackID = value2; 
    case value2 : return stackID = value3; 
    ... 
    case valueN : return stackID = value1; 
    } 
    assert(false); 
    return stackID; // some compilers might warn otherwise 
#endif 
} 

StackID operator++(StackID& stackID, int) 
{ 
    StackID tmp(stackID); 
    ++stackID; 
    return tmp; 
} 
+0

@sbi - Ciertamente me haría las cosas simples para hacer esto. pero, ¿qué le dice a los carteles que sugieren que un 'enum' está destinado a expresar un conjunto de valores arbitrarios? – BeeBand

+0

@BeeBand - si hay algún uso para el '++', supongo que tienes una buena razón para ir más allá del uso convencional de enums de C++ como citas verificadas en tiempo de compilación. Por otro lado, el código de Sbi podría hacer con algún control encuadernado. En Java, las enumeraciones son solo un tipo de clases livianas con miembros no modificables; se acepta la práctica de colocar métodos en ellas. Ver http://download.oracle.com/javase/1.5.0/docs/guide/language/enums.html. – tucuxi

+2

@BeeBand: No estoy de acuerdo. Probablemente, cinco de cada diez ejemplos de libros de texto enum son algo así como "mes enum {Jan, Feb, ..., Dec};' y por lo tanto un ejemplo perfecto para enenumeraciones que pueden incrementarse. Durante los últimos> 15 años de hacer C++, he sobrecargado 'operator ++' para enumeraciones probablemente menos de cinco veces. Pero lo he hecho. – sbi

1

Se supone que una enumeración semántica representa un conjunto de valores relacionados distintos.

lo que podría tener

enum Colour {RED, GREEN, BLUE}; 

Pero eso debe ser equivalente a:

enum Colour {GREEN, BLUE, RED}; 

El problema es que si incrementas una enumeración entonces esas representaciones no son los mismos. GREEN ++ en el primer caso no es lo mismo que GREEN ++ en el segundo.

Hacer que su programa dependa de la declaración de la enumeración es una receta para desastres: los encargados del mantenimiento pueden suponer que el orden de la enumeración no importa, introduciendo muchos errores silenciosos.

+1

pero es posible asignar explicitamente los valores para las enumeraciones por ejemplo 'color enum {RED = 0, VERDE = 1, BLUE = 2}'. En ese caso, el orden no importa. – Naveen

+0

Cierto, pero luego vas más allá de lo que debe ser una enumeración, y cruzando en ella un conjunto de pares clave/valor. – PaulJWilliams

+0

No está claro lo que sugieres cuando dices "eso debería ser equivalente", ¿puedes elaborar un poco? – BeeBand

1

Las enumeraciones van a ser tipo int, por lo que puede emitirlas. ¿Es esto lo que estás tratando de hacer?

int ndx = (int) StackID.SomeValue; 
... 
++ndx; 

Esto hará que alguien esté muy confundido en la línea, por supuesto.

Se me ocurre que está usando un enum donde debe usar const, o incluso #define. enum es más apropiado cuando tiene valores arbitrarios (donde el valor exacto no es significativo).

+0

Creo puedes tener razón, re. const. Mi código asume que el valor exacto es significativo, y como usted (y otros carteles) han sugerido, rompe el significado previsto de una enumeración. – BeeBand

6

casting de ida y vuelta a/desde int es, por supuesto, la solución obvia, entonces dejar en claro que entiende que la adición está sucediendo "fuera" de la enum:

nextAvail = static_cast<StackID>(static_cast<int>(nextAvail) + 1); 
+2

Hmm. Sí, esa era mi otra opción, solo creo que todos los actores hacen que el código parezca un poco desordenado. – BeeBand

0

Con respecto a oprator ++, $ 5.2 .6/1 dice: "El tipo de operando debe ser un tipo aritmético o un puntero a un tipo de objeto completo".

StackID no encaja aquí. Es del tipo enumeración.

Una opción es la siguiente

$ 5.7/1 - "Además, ambos operandos tendrán un tipo de enumeración o aritmética, o un operando será un puntero a un tipo de objeto completamente definido y el otro tendrá un tipo de enumeración o integral".

enum Possibility {Yes, No, Maybe}; 

Possibility operator++(Possibility const& r){ 
    return Possibility(r + 1); // convert r to integer, add 1, convert back to Enum 
} 

int main(){ 
    Possibility p = Yes; 
    Possibility p1 = ++p; 
} 
+0

Es bastante obvio (para el afiche también) que esto es ilegal, la pregunta era qué buen sustituto era –

+0

Si sobrecarga el incremento de __prefix__, siempre debe sobrecargar __postfix__incremento. – sbi

14

Porque las enumeraciones no tienen que ser contiguas. P.ej. tome este ejemplo:

enum Colors { 
cRed, // = 0 
cBlue, // = 1 
cGreen = 3 
} 

¿Qué debería pasar en este escenario?

Colors color = cBlue; 
Colors other = color++; 

Debería haber otra cGreen o debe ser 2. En ese caso, no es un miembro de la enumeración válida más. ¿Qué hay de esto?

Colors color = cGreen; 
Colors other = color++; 

En caso de ser othercRed (envolver) o 4?

Como puede ver, poder incrementar los valores de enumeración introduce un montón de preguntas y complica el mecanismo simple que pretenden ser.

Si todo lo que te preocupa es el valor entero que se incrementa, entonces simplemente transfórmalo a int e incrementa eso.

+2

@Igor: Ahora reemplace su ejemplo con esto: 'enum month ...'.Los meses son contiguos y se incrementan naturalmente. ¿Entonces que? – sbi

+8

@sbi: Oh meses, eso cambia todo porque los meses son simples. . . espera, ¿qué pasa cuando incrementas diciembre? ¿Debería arrojar una excepción de desbordamiento o debería envolverse? Puede ser obvio para ti lo que sucede, pero puedo requerir una funcionalidad diferente. Hay demasiadas preguntas acerca de cómo debería funcionar * esto *, los diseñadores del lenguaje estaban en lo correcto al deshabilitar esto, y dejar que cada caso se maneje individualmente. –

+0

@Igor: ¡Gracias! Creo que tu respuesta es extremadamente útil, sin embargo, he aceptado las de sbi. Creo que hice un poco de trampa aquí, básicamente hice 2 preguntas en mi publicación. Una de ellas está en el título, y creo que la has clavado en tu respuesta y la otra es la última línea de mi publicación, por lo que creo que los puntos van a sbi. – BeeBand

2

¿Por qué no almacenar nextAvail en su lugar como int si va a hacer operaciones aritméticas en él?

Otra opción sería envolver la enumeración en su propio tipo y sobrecargar operator ++ (que también podría envolver o algo así por ejemplo).

+0

Creo que eso es lo que busco: envolver la enumeración en mi propio tipo y luego puedo hacer lo que me gusta. ¿Puedes señalarme en la dirección de un ejemplo? – BeeBand

Cuestiones relacionadas