2009-11-15 12 views
5

Intento mantener las cosas lo más locales posible, así que pongo enumeraciones en el alcance de la clase, incluso si se comparten entre dos clases (lo puse en la clase que "va mejor" con él). funcionó muy bien, pero recientemente me encontré con un problema donde se producirá una dependencia circular si pongo la enumeración en el alcance de la clase.Enum scoping issues

La enumeración va a ser un argumento constructor para múltiples clases, y la clase en la que está (y la clase que tiene más sentido para que esté) incluye esas clases. Por lo tanto, no es posible usar la enumeración como un argumento de constructor para las clases incluidas porque dará como resultado una dependencia circular.

¿Sería mejor poner esta enumeración en su propio archivo de encabezado, y si es así, debería poner todas las enumeraciones en el archivo de encabezado para ser consistente? ¿Hay alguna otra solución a este problema (que sea lógica)?

+0

¿Has probado las declaraciones futuras? –

+0

Las declaraciones directas solo le permiten acceder al nombre del tipo. No puede acceder a la interfaz ni crear instancias ni hacer nada de eso. – Anonymous

Respuesta

3

si la enumeración es utilizado por múltiples clases, entonces yo diría que en realidad no tiene cabida en la definición de una sola clase, sino en el espacio de nombres en que esas clases residen.

esto a menos que la enumeración pase por una clase al constructor de otra, en cuyo caso puede tener más sentido crear una instancia de la clase dependiente enum por separado y pasarla como un parámetro al constructor de la clase contenedora.

+0

Lo segundo que describió es el caso, pero el método que describió no funcionará. Decidí simplemente poner las enumeraciones disponibles en un encabezado de utilidad que tenga métodos comúnmente utilizados, etc. – Anonymous

-2

Usted puede intentar reenviar declarar la enumeración de esta manera:

enum MyEnum; 
+2

Los mensajes no pueden ser anunciados. – Anonymous

+1

E incluso si pudiera, no puede reenviar los tipos anidados. O más bien, no puede reenviar declarar un tipo anidado sin tener la definición del tipo de anidamiento. –

+0

Como nota histórica, desde C++ 11 puede reenviar-declarar una 'clase enum' (o' enum struct'), pero estos son distintos de 'enum's de estilo antiguo. – Quackmatic

1

Debe colocar la enumeración fuera de cualquier clase si se comparte, pero aún puede determinar el alcance de la enumeración. Colocarlo en espacio de nombres por lo que los encuestadores no se "fuga", que saturan el espacio de nombres de su proyecto:

namespace Project { // you already have this, right? :) 
    namespace COLOR { // naming styles differ, use what you like 
    enum Color { 
     red, 
     green, 
     blue 
    }; 
    } 
    using COLOR::Color; // now you can use the type 'normally' 

    // examples: 
    struct A { 
    Color c; 
    A() : c(COLOR::red) {} 
    }; 
    void f(Color c) { 
    using namespace COLOR; 
    // inside this function, we no longer have to prefix COLOR:: 
    if (c == green) { 
     go(); 
    } 
    else if (c == red) { 
     stop(); 
    } 
    } 
} 
+0

Tendré que intentar usar la declaración 'using' s = como lo hace para ver si me gusta más que el 'typedef' mess que he estado usando. Creo que me gustará (pero puedo usar una convención de nomenclatura diferente de todas las mayúsculas para el tipo de enumeración). ¡Gracias! –

+0

Utilicé CAPS porque parecía que el Color se usaría más que COLOR :: rojo, o utilizaría el último a menudo y podría usar una directiva de uso en el alcance de la función. 'typedef COLOR :: Color Color;' es equivalente a 'usar COLOR :: Color;' aquí. –

2

A menudo dejo mis enumeraciones en un espacio de nombres para evitar que los distintos valores de enumeración de abarrotar el espacio de nombres global. Creo que esto es lo que estás tratando de hacer poniéndolos en una clase. Pero si no lo hacen 'encajan' así en una clase, un espacio de nombres funciona más o menos igual de bien para este propósito:

namespace FooSettings 
{ 
    enum FooSettings 
    { 
     foo, 
     bar 
    }; 
} 
typedef enum FooSettings::FooSettings FooSettingsEnum; 


int main() 
{ 
    FooSettingsEnum x = FooSettings::foo; 
}; 

Tengo un fragmento de editor que construye el contorno de una nueva enumeración dada simplemente su nombre , incluyendo la línea

typedef enum FooSettings::FooSettings FooSettingsEnum; 

que crea un typedef así que es un poco más fácil de leer para declarar variables con el tipo de la enumeración.

Sospecho que Stroustrup habría hecho nombres de valores de enumeración con el alcance de la enumeración si tuviera la oportunidad, pero la compatibilidad C forzó su mano (esto es solo especulación - tal vez algún día busque en D & E y vea si él menciona algo).

+1

Personalmente prefiero una estructura que un espacio de nombres porque una estructura se puede manipular a partir de plantillas. –

+0

@Matthieu - El uso de una estructura simplemente nunca vino a la mente. No puedo decir que he echado de menos no poder manipular las enumeraciones con plantillas, pero ciertamente no veo ninguna desventaja al usar una estructura para ajustar el alcance de la enumeración. Otra buena idea para mí. Gracias. –

+0

Intenté encapsular enumeraciones en un espacio de nombres y también en una estructura, y algunas cosas funcionan mejor con una estructura.Por lo tanto, a menudo hago lo siguiente: struct MyEnum {enum Values ​​{a, b}; }; typedef MyEnum :: Values ​​MyEnum_t; Ahora puedo usar 'MyEnum_t' para referirme a la enumeración como un tipo, y' MyEnum :: a', 'MyEnum :: b' para referirme a sus miembros como si la enumeración fuera un tipo agregado. Experimenté un tiempo antes de elegir las convenciones de nombres particulares aquí y con el tiempo he descubierto que lo anterior es lo mejor que puedo hacer. – Permaquid

7

utilizo una variante de lo que Michael y Roger hacen:

namespace Color 
{ 
    enum Type 
    { 
     red, 
     green, 
     blue 
    }; 
}; 

void paintHouse(Color::Type color) {...} 

main() 
{ 
    paintHouse(Color::red); 
} 

me parece Color::Type a ser más bonita y más auto-documentado que Color::Color o COLOR::Color. Si encuentra Color::Type demasiado detallado, puede usar Color::T.

No prefijo mis valores enumerados (es decir, COLOR_RED) porque el espacio de nombres alrededor de la enumeración se convierte en el prefijo.

He dejado de usar la convención ALL_CAPS para mis constantes con ámbito porque chocan con las macros en las bibliotecas C (por ejemplo, NULL). Las macros no están en el ámbito de los espacios de nombres que se definen en

+0

Ahora estoy considerando adoptar la sugerencia de Matthieu M de usar struct en lugar de namespace para poder pasarlo como un parámetro de plantilla. Sin embargo, no puedo pensar en una razón por la que pasaría Color en lugar de Color :: Tipo como parámetro de plantilla. –

11

Como C++ 11, se puede utilizar un enum class. (O enum struct - lo mismo, declararon de manera diferente), donde los valores de enumeración están en el ámbito del nombre de la enumeración . Por ejemplo, aquí hay una declaración válida de C++ 11.

enum class token_type { 
    open_paren, 
    close_paren, 
    identifier 
}; 

Para acceder a los valores de la enumeración, sin embargo, debe alcance correctamente usando el operador ::. Por lo tanto, esta es una asignación válida en C++ 11:

token_type type = token_type::close_paren; 

Pero esto no es:

token_type type = close_paren; 

Esto resuelve el conflicto de nombres, y significa que usted no tiene que utilizar espacio de nombres contenedor o struct solo para detener el alcance de los valores que se escapan a donde no deberían. Esto significa que la siguiente enumeración puede existir en el mismo alcance que token_type:

enum class other_enum { 
    block, 
    thingy, 
    identifier 
}; 

Ahora los dos valores denominados identifier en las dos estructuras diferentes no interfieren.

1

Estoy de acuerdo con Emile. Otra opción si está utilizando C++ 98 es utilizar struct en lugar de espacio de nombres, de la siguiente

struct Color 
{ 
    enum Type 
    { 
    red, 
    green, 
    blue 
    }; 
}; 

lo prefiero ya que idealmente me gustaría utilizar un espacio de nombres para representar un módulo que contiene múltiples clases, en lugar de sólo la determinación del alcance una enumeración ...