2009-11-26 10 views
59

¿Hay alguna manera en C++ de extender/"heredar" enumeraciones?¿Extensiones extendidas en C++?

I.E:

enum Enum {A,B,C}; 
enum EnumEx : public Enum {D,E,F}; 

o al menos definir una conversión entre ellos?

+2

que registró una C++ 11 respuesta a http://stackoverflow.com/questions/14503620/extending-enum-types/14508431 # 14508431 – dspeyer

+0

Hay un artículo muy interesante, con algún código, aquí: http://www.codeproject.com/Articles/32000/Improving-C-Enums-Adding-Serialization-Inheritance –

Respuesta

53

No, no lo hay.

enum son realmente lo malo en C++, y eso es desafortunado por supuesto.

Incluso el class enum introducido en C++ 0x no resuelve este problema de extensibilidad (aunque al menos hacen algunas cosas para la seguridad del tipo).

La única ventaja de enum es que no existen: ofrecen algún tipo de seguridad sin imponer ninguna sobrecarga de tiempo de ejecución ya que son sustituidas por el compilador directamente.

Si desea una bestia, que tendrá que trabajar a sí mismo:

  • crear una clase MyEnum, que contiene un int (básicamente)
  • crear constructores llevan el nombre de cada uno de los valores interesantes

ahora se puede extender su clase (la adición de constructores con nombre) a voluntad ...

Esa es una solución, sin embargo, nunca he foun da satistifying manera de tratar con una enumeración ...

+0

Por desgracia, estoy de acuerdo. Tantas veces he querido una forma segura de pasar banderas, pero las enumeraciones son realmente simples. –

-3

El siguiente código funciona bien.

enum Enum {A,B,C}; 
enum EnumEx {D=C+1,E,F}; 
+8

No funciona realmente . A sigue siendo un Enum y no un EnumEx. Es decir. EnumEx x = A; no podrá compilar sin un molde. –

+2

En realidad, me preocupan más las implicaciones en el sistema de tipos, que la indexación de los valores. – cvb

7

Si fueron capaces de crear una subclase de una enumeración que tendría que trabajar a la inversa.

El conjunto de instancias en una subclase es un subconjunto de las instancias en la superclase. Piensa en el ejemplo estándar de "Forma". La clase Shape representa el conjunto de todas las Formas. La clase Circle, su subclase, representa el subconjunto de Formas que son Círculos.

Para que sea consistente, una subclase de una enumeración debería contener un subconjunto de los elementos en la enumeración de la que hereda.

(Y no, C++ no soporta esto.)

+3

Esa es una interpretación muy específica de la herencia, que no se aplica necesariamente a todos los usos de la herencia. No creo que realmente explique por qué un compilador no podría admitir enumeración EnumEx: public Enum {D, E, F}; de modo que un EnumEx podría pasarse a una función esperando un Enum. –

+1

@jon: porque rompería el principio de sustitución de Liskov: D sería "un Enum (porque hereda de él), pero no tendría ninguno de los valores válidos de un Enum. Contradicción. las enumeraciones están diseñadas para ser "tipos enumerados"; el punto es que usted define el tipo definiendo todos los objetos válidos de ese tipo (en el caso de C/C++, en realidad, el mapa de los valores enumerados a todos los tipos válidos es un poco extraño e implica el tipo utilizado para representar la enumeración). Esta puede ser una interpretación muy específica de la herencia, pero es buena, y AFAIK informa el diseño de C++. –

+1

Como un ejemplo particular donde se rompería, supongamos que defino una 'enum A {1, 65525}; 'y el compilador decide usar una int sin signo de 16 bits para representarla. Ahora supongamos que defino 'enumEx: public Enum {131071};'. No hay forma de que este objeto de tipo EnumEx se pueda pasar como una instancia de Enum, de hecho se cortaría. Oops. Es por eso que necesita punteros en C++ para hacer polimorfismo en tiempo de ejecución. Supongo que C++ podría hacer que cada enumeración tenga el tamaño de la enumeración más grande posible. Pero conceptualmente hablando, el valor 131071 no debería ser una instancia válida de Enum. –

6

he resuelto de esta manera:

typedef enum 
{ 
    #include "NetProtocols.def" 
} eNetProtocols, eNP; 

Por supuesto, si se agrega un nuevo protocolo de red en el archivo NetProtocols.def, hay que recompilar, pero al menos es expandible.

2

http://www.codeproject.com/KB/cpp/InheritEnum.aspx repasa un método para crear una enumeración expandida.

+1

Un método muy peligroso. Si dos enumeraciones que se unen utilizan el mismo valor, esto las confunde silenciosamente. – dspeyer

+1

@dspeyer todavía trae una idea interesante a todo el debate. tu solución es una descendencia de esto. –

0

Sólo una idea:

Se podría tratar de crear una clase vacía para cada constante (tal vez poner a todos en el mismo archivo para reducir el desorden), cree una instancia de cada clase y utilizar los punteros a estas instancias como las "constantes".De esta forma, el compilador comprenderá la herencia y realizará cualquier conversión ChildPointer-to-ParentPointer necesaria al usar llamadas a funciones, Y usted todavía obtiene verificaciones de seguridad de tipo por parte del compilador para asegurarse de que nadie transfiera un valor int inválido a las funciones (lo que tendría para usar si usa el método de valor LAST para "extender" la enumeración).

Aunque no lo he pensado del todo, cualquier comentario sobre este enfoque es bienvenido.

Y trataré de publicar un ejemplo de lo que quiero decir tan pronto como tenga algo de tiempo.

0

yo probamos este, funciona, pero es un poco tedioso:

#include <iostream> 

struct Foo { 
protected: 
    int _v; 
    Foo(int i) : _v(i) {} 

public: 
    operator int() const { return _v; } 

    static Foo FOO() { return 5; }; 
}; 

struct Bar : public Foo { 
    using Foo::Foo; 
    Bar(const Foo &f) : Foo(f) {} 

    static Bar BAR() { return 6; }; 
}; 

int main(int argc, const char *argv[]) { 
    // Bar bar = Bar::BAR(); 
    Foo foo = Foo::FOO(); 
    Bar bar = Bar::BAR(); 

    // 5 
    Bar b = foo; 
    std::cout << (int)b << std::endl; 

    // 6 
    b = bar; 
    std::cout << (int)b << std::endl; 

    // 5 
    b = foo; 
    std::cout << (int)b << std::endl; 

    // Foo err1(5); 
    // Bar err2(5); 
    return 0; 
} 
Cuestiones relacionadas