2009-11-26 18 views
59

C'è un modo in C++ per estendere/enumerazioni "Eredita"?enumerazioni si estende in C++?

cioè:

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

o almeno definire una conversione tra di loro?

+2

ho postato un C++ 11 risposta a http://stackoverflow.com/questions/14503620/extending-enum-types/14508431 # 14508431 – dspeyer

+0

C'è un articolo molto interessante, con un po 'di codice, qui: http://www.codeproject.com/Articles/32000/Improving-C-Enums-Adding-Serialization-Interitance –

risposta

53

No, non c'è.

enum sono davvero la cosa povera in C++, e questo è un peccato, naturalmente.

Anche lo class enum introdotto in C++ 0x non risolve questo problema di estensibilità (sebbene facciano almeno alcune cose per la sicurezza del tipo).

L'unico vantaggio di enum è che non esistono: offrono un certo tipo di sicurezza mentre non impongono alcun sovraccarico di runtime in quanto vengono sostituiti direttamente dal compilatore.

Se si desidera una bestia, si dovrà lavorare da soli:

  • creare una classe MyEnum, che contiene un int (sostanzialmente)
  • creare costruttori chiamati per ciascuno dei valori interessanti

ora è possibile estendere la classe (l'aggiunta di costruttori di nome) a volontà ...

Questa è una soluzione, però, non ho mai foun da satistifying modo di trattare con un'enumerazione ...

+0

Ahimè, sono d'accordo. Così tante volte ho voluto un modo sicuro per far passare le bandiere ecc. Ma le enumerazioni sono davvero solo intere. –

-3

Il seguente codice funziona bene.

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

Non funziona davvero . A è ancora un Enum e non un EnumEx. Cioè EnumEx x = A; non riuscirà a compilare senza un cast. –

+2

In realtà, sono più preoccupato per le implicazioni sul sistema di tipi, piuttosto che per l'indicizzazione dei valori. – cvb

7

Se tu fossi in grado di creare una sottoclasse di un enum che avrebbe dovuto lavorare il contrario.

Il set di istanze in una sottoclasse è un sottoinsieme delle istanze nella super-classe. Pensa all'esempio standard "Shape". La classe Shape rappresenta l'insieme di tutte le forme. La classe Circle, la sua sottoclasse, rappresenta il sottoinsieme di forme che sono cerchi.

Quindi, per essere coerente, una sottoclasse di un enum avrebbe dovuto contenere un sottoinsieme degli elementi della enum cui eredita.

(E no, C++ non supporta questa.)

+3

Questa è un'interpretazione molto specifica dell'ereditarietà, che non si applica necessariamente a tutti gli usi dell'ereditarietà. Non penso davvero spieghi perché un compilatore non possa supportare enum EnumEx: public Enum {D, E, F}; tale che un EnumEx possa essere passato a una funzione che si aspetta un Enum. –

+1

@jon: perché spezzerebbe il principio di sostituzione di Liskov: D sarebbe "essere un Enum" (perché eredita da esso), ma non avrebbe alcun valore valido di un Enum. Contraddizione. le enumerazioni sono progettate per essere "tipi enumerati" - il punto è che definisci il tipo definendo tutti gli oggetti validi di quel tipo (nel caso di C/C++, in realtà la mappa dai valori elencati a tutti i tipi validi è un po 'strana e coinvolge il tipo usato per rappresentare l'enum). Questa potrebbe essere un'interpretazione molto specifica dell'ereditarietà, ma è buona, e AFAIK informa il design di C++. –

+1

Come esempio particolare in cui si romperebbe, supponiamo di definire un enum A {1, 65525}; 'e il compilatore decide di usare un int unsigned a 16 bit per rappresentarlo. Supponiamo ora di definire 'enumEx: public Enum {131071};'. Non c'è modo in cui questo oggetto di tipo EnumEx possa essere passato come un'istanza di Enum, sarebbe in effetti affettato. Ops. Questo è il motivo per cui è necessario puntatori in C++ per eseguire il polimorfismo di runtime. Immagino che C++ possa rendere ogni enum la dimensione del più grande enum possibile. Ma concettualmente, il valore 131071 non dovrebbe essere un'istanza valida di Enum. –

6

ho risolto in questo modo:

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

Naturalmente, se si aggiunge un nuovo protocollo di rete nel file NetProtocols.def, si deve ricompilare, ma almeno è espandibile.

2

http://www.codeproject.com/KB/cpp/InheritEnum.aspx passa a un metodo per creare un enum espanso.

+1

Un metodo molto pericoloso. Se due enum messi insieme usano lo stesso valore, questo li confonde in silenzio. – dspeyer

+1

@dspeyer porta ancora un'idea interessante all'intero dibattito. la tua soluzione è una prole di questo. –

0

Solo un'idea:

Si potrebbe provare a creare una classe vuota per ogni costante (forse metterli tutti nello stesso file per ridurre l'ingombro), creare un'istanza di ogni classe e utilizzare i puntatori a queste istanze come le "costanti".In questo modo, il compilatore comprenderà l'ereditarietà e eseguirà qualsiasi conversione di ChildPointer-to-ParentPointer necessaria quando si utilizzano le chiamate di funzione, E si ottiene comunque controlli di sicurezza del tipo da parte del compilatore per garantire che nessuno passi un valore int non valido alle funzioni (che avrebbe da utilizzare se si utilizza il metodo LAST value per "estendere" l'enum).

Non ho ancora pensato a tutto questo, quindi qualsiasi commento su questo approccio è benvenuto.

E proverò a pubblicare un esempio di cosa intendo appena ho un po 'di tempo.

0

Ho provato questo, funziona, ma è un po 'noioso:

#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; 
}