2009-05-23 11 views
10

Así que aquí es mi problema:C++ typedef enum de otra clase?

struct A 
{ 
    enum A_enum 
    { 
     E0, 
     E1, 
     E2 
    }; 
}; 

struct B 
{ 
    typedef A::A_enum B_enum; 
    bool test(B_enum val) 
    { 
     return (val == E1); // error: "E1" undeclared identifier 
    } 
}; 

que específicamente no quiero decir A::E1. Si intento B_enum::E1 recibo una advertencia de que no es estándar. ¿Hay una buena manera de hacer algo como esto?

+2

"Específicamente, no quiero decir A :: E1", ¿por qué no? –

+1

porque A se usa en varias clases y no quiero referirme a un tipo no relacionado. En mi uso real, A es un tipo anidado que es typedefed en otro lugar, y realmente sería más como N :: E :: C :: A :: E1 – rlbond

+0

Luego tendrá que hacer un espacio de nombre y definir la enumeración ahí. ¿Por qué no haces eso? –

Respuesta

3

Creo que A debe ser un espacio de nombres en lugar de una estructura.

+0

Bueno, A tiene otras cosas también, pero tal vez sería mejor ponerlo en un espacio de nombres. – rlbond

+1

También puede definir una enumeración en el ámbito global ... o, en una clase base heredada por las clases que la utilizan. – ChrisW

-2

¿Por qué incluso tienes el método de prueba en struct B? No creo que tenga ningún sentido.

Al definir una estructura A y definir una enumeración dentro de ella, está diciendo más o menos que "aquí hay una enumeración en el alcance de la estructura A" y esto es lo mismo que decir "si quiere usar esta enumeración, tiene para referirse a la estructura A ".

Poner la enumeración dentro de un espacio de nombres no resolverá su problema. Porque tendrá el mismo problema de alcance (C4482)

Creo que está haciendo las cosas demasiado complicadas. ¿Qué piensas de esto?

struct A 
{ 
    enum A_enum 
    { 
     E0, 
     E1, 
     E2 
    }; 

    static bool test(A_enum val) 
    { 
     return (val == E1); 
    } 
}; 


int main() 
{ 
    assert(A::test(A::E1)); 
    return 0; 
} 

Tenga en cuenta que A :: test() es estático. Debería ser porque no estás operando en el estado struct. Y dado que es estático, no necesita una instancia de A para llamar al método.

+1

Es un ejemplo. El caso real es más complicado. No, no puedo hacerlo de esa manera. – rlbond

2

Poner enum en alcance global está demasiado expuesto, ponerlos en una clase puede introducir una dependencia no deseada. Para enumeración no vincula estrechamente a una clase, esto es lo que yo uso:

#define CLEANENUMS_BEGIN(name) namespace name { typedef enum { 
#define CLEANENUMS_END(name) } internal_ ## name ## _e;} typedef name::internal_ ## name ## _e name ## _e; 

entonces usted puede utilizar, en el ámbito global:

CLEANENUMS_BEGIN(myEnum) 
    horizontal, 
    vertical, 
CLEANENUMS_END(myEnum) 

Eso es más o menos emulando C# forma de manejar las enumeraciones alcance . El preprocesador producirá este código:

namespace myEnum 
{ 
    enum internal_myEnum_e 
    { 
     horizontal, 
     vertical, 
    } 
} 
typedef internal_myEnum_e myEnum_e; 

A continuación, una enumeración dada se hace referencia como

myEnum_e val = myEnum::horizontal; 

Con suerte hay una mejor forma de hacer esto, pero hasta el momento, que es la única solución que encontré.

+0

Cuando venga C++ 0x ... siempre que sea ... habrá "clase enum MyEnum {...};" eso funciona como el enum de C# (con los valores enum en un "espacio de nombre" propio). De hecho, la sintaxis se tomó de C++ /. CLI (el .NET C++) donde se usa para emitir enumeraciones .NET. – mmmmmmmm

+0

¿Cómo se refiere a los elementos? – rlbond

+0

puede decir EnumName :: Enumerator, y también puede declararlos en otro lugar utilizando una declaración using como "using EnumName :: Enumerator;" para hacer visible sin prefijación con EnumName ::. esto viene con la nueva especificación de tipo subyacente, de todos modos. para "clase enum", el valor predeterminado es int. Puede cambiarlo con "enum class EnumName: IntegerType {...};" y los mismos trabajos, por supuesto, también para las enumeraciones sin ámbito "enumeración EnumName: int {...}", donde no hay ningún valor predeterminado. Con eso, la posibilidad de "declarar hacia adelante" también viene: "enum EnumName: int;". –

0

He tenido el mismo problema en el pasado, y así es como lo he solucionado. Quería poder cambiar la implementación de una biblioteca en tiempo de compilación. Uno de los lib estaba usando código como este:

namespace Lib1 
{ 
    enum LibEnum { One, Two, Three }; 
    [...] 
    void someFunc(LibEnum val); 
} 

En mi código, quería ocultar la implementación de la biblioteca de la experiencia del usuario (lo que un usuario de mi código nunca debe ver lo lib estoy usando internamente):

Solución 1:

namespace MyCode 
{ 
    // Example to avoid copying a function from Lib1 here 
    typedef Lib1::someFunc aFunctionImUsing; 

    // This doesn't work 
    // typedef LibEnum MyEnum; 
    // As such code doesn't compile: 
    // aFunctionImUsing(One); // Error unknown identifier One 
    // But this did: 
    struct Type 
    { 
    enum MyType { One = Lib1::One, Two = Lib1::Two, Three = Lib1::Three }; 
    } 
    static inline Lib1::LibEnum as(Type::MyType t) { return (Lib1::LibEnum)t; } 

    // Now code like this compiles: 
    aFunctionImUsing(as(Type::One)); 
    // This one doesn't: 
    // aFunctionImUsing(Type::One); // Can't convert from Type::MyType to Lib1::LibEnum 

    [...] 
} 

Solución 2:

namespace MyCode 
{ 
    struct Type 
    { 
    enum MyType { One = Lib1::One, Two = Lib1::Two, Three = Lib1::Three }; 
    } 

    // If you don't care about polluting your namespace with numerous wrapper 
    // you can write this instead of typedef someFunc: 
    static inline void myFunc(Type::MyType t) { return Lib1::someFunc((Lib1::LibEnum)t); } 

    // This one does: 
    myFunc(Type::One); 
    [...] 
} 

Son 2 problemas con el fragmento de código anterior.El primer problema es que debe copiar & pegue la enumeración dentro de su espacio de nombres, (pero con una expresión regular simple en find & replace, ya está). El segundo problema es que su usuario tendrá que usar el método "como", lo que significa que no es sencillo, o tiene que ajustar el método/función con la segunda solución.

De todos modos, como no es posible inyectar una enumeración en el espacio de nombre, esta solución es lo mejor que puede hacer. Tenga en cuenta que su usuario de código ni siquiera sabe que está utilizando la biblioteca Lib1 en el código de ejemplo.

0

Tuve el mismo problema, y ​​utilicé esta solución en lugar de fastidiar con espacios de nombres superfluos. Mantiene la enumeración segura dentro de la clase y sus usuarios explícitos.

class TreeWindow abstract { 
public: 
    enum CheckState { On, Off, Partial } 
}; 

class TreeControl abstract { 
public: 
    class ItemChecked abstract: public TreeWindow { // this is the hack needed 
    public: 
    using TreeWindow::CheckState; 
    }; 

    void func(ItemChecked::CheckState); 
}; 

TreeControl& tree = ... 
tree.func(TreeControl::ItemChecked::Partial); 
+4

Creo que este es el idioma equivocado ... – rlbond

0

Parece que no hay una solución simple. Me molesta también. Si puede cambiar A, existe una solución que no depende de los espacios de nombres si no se desea la incorporación de la enumeración a un alcance fuera de A. En contraste con el uso de espacios de nombres, tanto A como A_Enum pueden ser clases anidadas.

struct A_Enum 
{ 
    enum A_enum 
    { 
     E0, 
     E1, 
     E2 
    }; 
}; 

struct A : public A_Enum 
{ 
    using A_Enum::A_enum; 
}; 

struct B : public::A_Enum 
{ 
    using A_Enum::A_enum; // original enum name 
    bool testA(A_enum val) { return (val == E1); } 
}; 

EDIT2: eliminé la segunda solución de nuevo, no funciona como pensé.