2010-03-19 8 views
9

¿Qué tan horrible es, o es perfectamente aceptable, indexar un bucle en una enumeración?Bucle en valores de enumeración

Tengo una enumeración definida. Los valores de los literales son valores predeterminados. Los valores asignados no tienen ningún significado, no tendrán ningún significado, y los valores de cualquier literal agregado en el futuro tampoco tendrán ningún significado. Se acaba de definir para limitar los valores permitidos y facilitar el seguimiento. Por lo tanto, los valores siempre van a comenzar en 0 y aumenta en 1.

¿Puedo establecer un bucle de este modo:

enum MyEnum 
{ 
    value1, 
    value2, 
    value3, 
    maxValue 
} 

for(MyEnum i = value1; i < maxValue; i = static_cast<MyEnum>(i+1)){} 
+1

Stop! Me estás haciendo sentir nostálgico por Pascal. –

Respuesta

4

En lo que a mí respecta, eso está bien. Estoy seguro de que algunos puristas de algún lugar se asustarán, pero en lo que se refiere a la especificación del lenguaje, ese código funcionará correctamente, por lo que debes sentirte libre de hacerlo si te hace la vida más fácil.

0

Eso está bien siempre y cuando no establezca específicamente el valor de un valor enum. Su ejemplo está muy bien, pero esto podría ser malo:

// this would be bad 
enum MyEnum 
{ 
    value1, 
    value2 = 2, 
    value3, 
    maxValue 
}; 

Como los estados estándar de 7.2/1:

Un enumerador definición sin un inicializador da el empadronador el valor obtenido mediante el aumento de la valor del enumerador anterior en uno.

+2

Es por eso que especifiqué que los valores de los literales no son significativos y nunca lo serán. Obtienen los valores predeterminados. – Rachel

+1

@Rachel - Solo estaba tratando de ser claro sobre lo que podría salir mal. Como dije en la primera oración de mi respuesta, está perfectamente bien la forma en que lo haces. –

0

No conozco ningún uso específico de esto que sea realmente útil y creo que es un código incorrecto. ¿Qué pasa si uno pone cosas como esta como se ve en muchas bibliotecas?

enum MyEnum 
{ 
    value1, 
    value2, 
    value3, 
    maxValue = 99999; 
}; 
+0

Entonces romperían maliciosamente mi código. – Rachel

+0

IMO, esa es una buena razón para agregar un comentario a la enumeración: dígales que no lo hagan. – Steve314

2

Puede ser útil hacer esto.

enum MyEnum 
{ 
    first, 
    value1, 
    value2, 
    value3, 
    last 
} 
+1

Eso le da "primero" su propio valor único. Las expresiones idiomáticas normales están semiabiertas, incluidas al inicio y exclusivas al final. Sugiero usar "first = value1" al final de la enumeración, o simplemente renombrar "value1" de forma que quede claro que es y siempre será el primero. – Steve314

0

Por lo tanto los valores empezarán siempre a 0 y aumentar en 1.

Tenga en cuenta que a menos que esto se escribe en el estándar de codificación en la que trabaja, cuando llega un programador de mantenimiento a lo largo de 6 meses y hace un cambio a una enumeración como:

enum MyEnum 
{ 
    value1, 
    value2 = 5, 
    value3, 
    maxValue 
} 

debido a un nuevo requisito, que obtendrá de explicar por qué su cambio legítimo rompió la aplicación.

Para fijar los valores de nombres que podría utilizar un mapa:

typedef std::map<std::string,int> ValueMap; 
ValueMap myMap; 
myMap.insert(make_pair("value1", 0)); 
myMap.insert(make_pair("value2", 1)); 
myMap.insert(make_pair("value3", 2)); 

for(ValueMap::iterator iter = theMap.begin(); iter != theMap.end(); ++iter) 
{ 
    func(iter->second); 
} 

Si los nombres no importan se puede utilizar una variedad de estilo C:

int values[] = { 0, 1, 2 }; 
int valuesSize = sizeof(values)/sizeof(values[0]); 

for (int i = 0; i < valuesSize; ++i) 
{ 
    func(values[i]); 
} 

o un método similar usando un std :: vector.

También, si lo que dice es verdad, y the values will always start at 0 and increase by 1 entonces estoy confundido en cuanto a por qué esto no funcionaría:

const int categoryStartValue = 0; 
const int categoryEndValue = 4; 

for (int i = categoryStartValue; i < categoryEndValue; ++i) 
{ 
    func(i); 
} 
-1

Usted puede envolver el cuerpo del bucle con una sentencia switch para proteger contra los no -los valores fundamentales Es potencialmente super lento, como en maxValue = 9999; caso, pero puede que no sea lo peor que haya pasado. Veo este estilo aquí todo el tiempo en nuestras bases de código

enum MyEnum { 
value1, 
value2, 
value3, 
maxValue 
} 
for(MyEnum i = value1; i < maxValue; i = static_cast<MyEnum>(i+1)){ 
    switch(i) 
    { 
     case value1: 
     case value2: 
     case value3: 
        //actual code 
    } 

} 
+0

¡Ahhh! ¡El temido patrón For/Case! http://thedailywtf.com/Articles/The_FOR-CASE_paradigm.aspx http://thedailywtf.com/Articles/Switched_on_Loops.aspx –

0

No es fácil ...

La única solución que he encontrado es realmente implementar una clase sobre la enumeración, y luego usar una macro para definir la enumeración ...

DEFINE_NEW_ENUM(MyEnum, (value1)(value2)(value3)) 

la idea básica es la de almacenar los valores en un contenedor STL, almacenado de forma estática en una clase de plantilla en función del símbolo MyEnum. De esta forma se obtiene una iteración perfectamente definida, e incluso se obtiene un estado inválido en vano (debido al end()).

Usé una ordenada vector de pair<Enum,std::string> para poder beneficiarme de la impresión bonita en los registros y la serialización de larga duración (y porque es más rápido que un contenedor asociativo para pequeños conjuntos de datos y ocupa menos memoria).

No he encontrado una mejor manera todavía, ¿C++ 11 finalmente obtiene iteración en enumeraciones o no?

8

me escribió un iterador enumeración hace un tiempo para estos casos

enum Foo { 
A, B, C, Last 
}; 

typedef litb::enum_iterator<Foo, A, Last> FooIterator; 

int main() { 
    FooIterator b(A), e; 
    std::cout << std::distance(b, e) << " values:" << std::endl; 
    std::copy(b, e, std::ostream_iterator<Foo>(std::cout, "\n")); 

    while(b != e) doIt(*b++); 
} 

Si usted está interesado, aquí está el código. Si lo desea, puede ampliarlo para que sea un iterador de acceso aleatorio al proporcionar +, <, [] y sus amigos. Algoritmos como std::distance le agradecerán al proporcionar O(1) complejidad de tiempo para el iterador de acceso aleatorio.

#include <cassert> 

namespace litb { 

template<typename Enum, Enum Begin, Enum End> 
struct enum_iterator 
    : std::iterator<std::bidirectional_iterator_tag, Enum> { 
    enum_iterator():c(End) { } 
    enum_iterator(Enum c):c(c) { } 

    enum_iterator &operator=(Enum c) { 
    this->assign(c); 
    return *this; 
    } 

    enum_iterator &operator++() { 
    this->inc(); 
    return *this; 
    } 

    enum_iterator operator++(int) { 
    enum_iterator cpy(*this); 
    this->inc(); 
    return cpy; 
    } 

    enum_iterator &operator--() { 
    this->dec(); 
    return *this; 
    } 

    enum_iterator operator--(int) { 
    enum_iterator cpy(*this); 
    this->dec(); 
    return cpy; 
    } 

    Enum operator*() const { 
    assert(c != End && "not dereferencable!"); 
    return c; 
    } 

    bool equals(enum_iterator other) const { 
    return other.c == c; 
    } 

private: 
    void assign(Enum c) { 
    assert(c >= Begin && c <= End); 
    this->c = c; 
    } 

    void inc() { 
    assert(c != End && "incrementing past end"); 
    c = static_cast<Enum>(c + 1); 
    } 

    void dec() { 
    assert(c != Begin && "decrementing beyond begin"); 
    c = static_cast<Enum>(c - 1); 
    } 

private: 
    Enum c; 
}; 

template<typename Enum, Enum Begin, Enum End> 
bool operator==(enum_iterator<Enum, Begin, End> e1, enum_iterator<Enum, Begin, End> e2) { 
    return e1.equals(e2); 
} 

template<typename Enum, Enum Begin, Enum End> 
bool operator!=(enum_iterator<Enum, Begin, End> e1, enum_iterator<Enum, Begin, End> e2) { 
    return !(e1 == e2); 
} 

} // litb 
+0

Eso es astuto. :) Solo tienes que arrojar el operador de coma allí en algún lugar para tu firma. – GManNickG

+0

¿Por qué 'operator ==' pero no 'operator>' (y friends)? –

+0

@Chris no lo necesitaba en ese momento, ya que mi objetivo era la iteración del rango, así que fui a implementar la interfaz bidireccional-iterador. Pero un 'op>' ciertamente tendría sentido para este iterador enum. –

0

por lo general definen comenzar y los valores finales en mis enumeraciones cuando necesito iteración, con una sintaxis especial como lo siguiente (con comentarios del estilo de doxygen para aclarar):

enum FooType { 
    FT__BEGIN = 0,  ///< iteration sentinel 
    FT_BAR = FT__BEGIN, ///< yarr! 
    FT_BAZ,    ///< later, when the buzz hits 
    FT_BEEP,    ///< makes things go beep 
    FT_BOOP,    ///< makes things go boop 
    FT__END    ///< iteration sentinel 
} 

FT_ está ahí para ayudar con espacios de nombres (para evitar enfrentamientos entre enumeraciones) y los doble guiones bajos ayudan a distinguir entre los valores reales y los centinelas de iteración.

Nota al margen:

Escribir este comienzo a la preocupación acerca de la doble guión no está permitido para identificador de usuario (que es la aplicación reservado, IIRC?), Pero estoy todavía para golpear un problema (después de 5-6 años de codificación en este estilo). Podría estar bien, ya que siempre pongo todos mis tipos en un espacio de nombres para evitar contaminar e interferir con el espacio de nombres global.

Cuestiones relacionadas