2009-08-18 14 views
12

tengo algo de código C++, en la que se declara la siguiente enumeración:¿Existe algún paradigma bien conocido para iterar los valores enum?

enum Some 
{ 
    Some_Alpha = 0, 
    Some_Beta, 
    Some_Gamma, 
    Some_Total 
}; 
int array[Some_Total]; 

Los valores de Alfa, Beta y Gamma son secuenciales, y con mucho gusto utilizar el siguiente ciclo para iterar a través de ellos:

for (int someNo = (int)Some_Alpha; someNo < (int)Some_Total; ++someNo) {} 

Este ciclo está bien, hasta que decida cambiar el orden de las declaraciones en la enumeración, por ejemplo, haciendo que Beta sea el primer valor y Alpha, el segundo. Eso invalida el encabezado del ciclo, porque ahora tengo que iterar de Beta a Total. Entonces, ¿cuáles son las mejores prácticas de iteración a través de enum? Quiero iterar a través de todos los valores sin cambiar los encabezados de ciclo cada vez. No puedo pensar en una solución:

enum Some 
{ 
    Some_Start = -1, 
    Some_Alpha, 
    ... 
    Some_Total 
}; 
int array[Some_Total]; 

e iterar a partir de (Start + 1) al total, pero parece feo y nunca he visto a alguien hacerlo en el código. ¿Existe algún paradigma bien conocido para iterar a través de la enumeración, o simplemente tengo que fijar el orden de los valores enum? (Vamos a suponer que, realmente tengo algunas razones impresionantes para cambiar el orden de los valores de enumeración) ...

+0

Podría dar un ejemplo de cuando esto es necesario? ¿O es esto puramente teórico? – Zed

+0

Mi código depende de las decisiones del equipo de los diseñadores. A veces cambian de opinión sobre el orden de los elementos presentes en la pantalla. Tengo que actualizar mis valores enum de acuerdo con el orden actual de los elementos ... – SadSido

+0

Normalmente, no cambiaría el orden; si lo hace, las cosas que dependen de ese orden (si está utilizando enumeraciones en ambos extremos de un sistema de comunicación, escribiendo los valores en un archivo/base de datos) se romperá. –

Respuesta

0
enum Some 
{ 
    Some_first_ = 0, 
    Some_Alpha = Some_first_, 
.... 
    Some_last_ 
}; 

Haciendo tal que pueda otorgar a primera & última nunca cambia para

+11

¿Por qué los doble guiones bajos? Esos nombres son ilegales en C++. –

+0

¿Qué quieres decir con ilegal? Solo se usa para acentuar sus propósitos especiales. Y SadSido preguntó sobre el paradigma, y ​​nadie te obliga a violar la convención del código. – Dewfy

+6

Los nombres que contienen el guión bajo doble están reservados por el Estándar C++ para la implementación C++; no tiene permitido usarlos en su propio código. –

0

Si no se utiliza ningún asignaciones, las enumeraciones están garantizadas para ser secuenciales comenzando con 0 como el primero. thers.

Lo mejor que puede hacer es mantenerlos en el orden que desee en su definición de enumeración, y recorrerlos con el ciclo for.

4

No, no hay manera de hacer esto porque no hay ninguna garantía de que alguien no ha escrito un código como:

enum Some 
{ 
    Some_Alpha = 0, 
    Some_Beta, 
    Some_Gamma = 42, 
    Some_Delta, 
    Some_Total 
}; 
+1

Como ha señalado sbi, en realidad hay una forma de hacerlo que puede manejar este tipo de casos sin problemas. –

3

Se puede extraer de este article con su source code de cómo se puede aplicar esto con miembros estáticos de la clase.

+0

+1, las enumeraciones son un tipo de datos demasiado primitivo para iterar sobre ellas, dr. Dobb viene al rescate. – Ozan

+0

¡Gran artículo, gracias! Recuerdo la discusión del patrón "enum de tipo seguro" en algún libro de Java y esto es lo mismo para C++ – SadSido

0

Coloque todos los mensajes en su propio espacio de nombres. Ejemplo:

namespace Foo { 
enum Enum { 
    First=0, // must be sequential 
    Second, 
    Third, 
    End // must be last 
}; 
} 

En código:

for (int i=Foo::First; i!=Foo::End; i++) { 
// do stuff 
} 

Esto es debido a que C++ permite cosas como esta (no probado en un compilador):

enum Foo { 
Alpha = 1 
}; 

enum Bar { 
Beta = 2 
}; 

Foo foo = Beta; 

que está claramente equivocado.

+1

Casi mis enum se declaran dentro de clases, que proporcionan el alcance. En C++, la clase es la unidad básica de alcance, no el espacio de nombres. –

+0

Una clase que existe solo para contener una enumeración no se siente bien. –

+1

Bueno, tampoco lo hace un espacio de nombres, en lo que a mí respecta. –

13

Puede definir un operator++() para su enum. Esto tiene la ventaja de que utiliza el conocido paradigma de los operadores de incremento estándar. :)

Dependiendo de si sus enumeraciones son contiguos, puede tratarlos como int o usar un interruptor:

Some& operator++(Some& obj) 
{ 
# if YOUR_ENUMS_ARE_CONTIGUOUS 
    int i = obj; 
    if(++i > Some_Total) i = Some_Alpha; 
    return obj = static_cast<Some>(i); 
# else 
    switch(obj) 
    { 
    case Some_Alpha : obj = Some_Beta; break; 
    case Some_Beta : obj = Some_Gamma; break; 
    case Some_Gamma : obj = Some_Total; break; 
    case Some_Total : obj = Some_Alpha; break; 
    default: assert(false); // changed enum but forgot to change operator 
    } 
    return obj; 
# endif 
} 

Tenga en cuenta que, si se ha definido operator++(), los usuarios probablemente esperar un operator--(), también.

1

En 11 (y probablemente antes), se puede utilizar este truco C++, para hacer Some iterable:

Some operator++(Some& s) { 
    return s = (Some)(std::underlying_type<Some>::type(x) + 1); 
} 
Some operator*(Some s) { 
    return s; 
} 
Some begin(Some s) { 
    return Some_Alpha; 

Some end(Some s) { 
    return Some_Gamma; 
} 

int main() { 
    // the parenthesis here instantiate the enum 
    for(const auto& s : Some()) { 
     // etc. etc. 
    } 
    return 0; 
} 

(esta pregunta sin pudor una adaptación de here.)

Cuestiones relacionadas