Inoltra dichiarando un enum in C ++


265

Sto cercando di fare qualcosa di simile al seguente:

enum E;

void Foo(E e);

enum E {A, B, C};

che il compilatore rifiuta. Ho dato una rapida occhiata a Google e il consenso sembra essere "non puoi farlo", ma non riesco a capire perché. Qualcuno può spiegare?

Chiarimento 2: lo sto facendo in quanto ho metodi privati ​​in una classe che accetta detto enum e non voglio che i valori dell'enum siano esposti - quindi, per esempio, non voglio che nessuno sappia che E è definito come

enum E {
    FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X
}

poiché il progetto X non è qualcosa che voglio che i miei utenti sappiano.

Quindi, volevo inoltrare l'enum in modo da poter mettere i metodi privati ​​nel file di intestazione, dichiarare l'enum internamente nel cpp e distribuire il file della libreria e l'intestazione creati alle persone.

Per quanto riguarda il compilatore, è GCC.


Sono trascorsi così tanti anni e in qualche modo StackOverflow mi ha attirato indietro;) Come suggerimento post mortem - semplicemente non farlo specialmente nello scenario che descrivi. Preferirei definire un'interfaccia astratta ed esporre questo tot agli utenti e mantenere la definizione enum e tutti gli altri dettagli dell'implementazione con l'implementazione interna che nessun altro vede dalla mia parte permettendomi di fare qualunque cosa in qualsiasi momento e avere il pieno controllo di quando gli utenti vedono nulla.
RnR

Se leggi oltre la risposta accettata, questo è completamente possibile dal C ++ 11.
fuzzy Pochi

Risposte:


217

Il motivo per cui l'enum non può essere dichiarato in avanti è che senza conoscere i valori, il compilatore non può conoscere la memoria richiesta per la variabile enum. I compilatori C ++ possono specificare lo spazio di archiviazione effettivo in base alle dimensioni necessarie per contenere tutti i valori specificati. Se tutto ciò che è visibile è la dichiarazione a termine, l'unità di traduzione non può sapere quale dimensione di archiviazione sarà stata scelta - potrebbe essere un carattere o un int o qualcos'altro.


Dalla sezione 7.2.5 della norma ISO C ++:

Il tipo sottostante di un'enumerazione è un tipo integrale che può rappresentare tutti i valori dell'enumeratore definiti nell'enumerazione. È definito dall'implementazione quale tipo integrale viene utilizzato come tipo sottostante per un'enumerazione, tranne per il fatto che il tipo sottostante non deve essere maggiore di a intmeno che il valore di un enumeratore non possa rientrare in un into unsigned int. Se l' elenco di enumeratori è vuoto, il tipo sottostante è come se l'enumerazione avesse un singolo enumeratore con valore 0. Il valore di sizeof()applicato a un tipo di enumerazione, un oggetto di tipo di enumerazione o un enumeratore, è il valore di sizeof()applicato al tipo sottostante.

Dal momento che il chiamante della funzione deve conoscere le dimensioni dei parametri per impostare correttamente lo stack di chiamate, il numero di enumerazioni in un elenco di enumerazioni deve essere noto prima del prototipo della funzione.

Aggiornamento: in C ++ 0X è stata proposta e accettata una sintassi per la dichiarazione anticipata dei tipi di enum. Puoi vedere la proposta su http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdf


29
-1. Il tuo ragionamento non può essere corretto - altrimenti, perché ti è permesso di dichiarare "classe C;" e quindi dichiarare un prototipo di funzione che accetta o restituisce una C, prima di definire completamente C?
j_random_hacker,

112
@j_random: non puoi usare una classe prima che sia completamente definita - puoi usare solo un puntatore o un riferimento a quella classe e questo perché le loro dimensioni e modalità operative non dipendono da quale classe sia.
RnR

27
La dimensione di un riferimento o di un puntatore a un oggetto di classe viene impostata dal compilatore e indipendentemente dalla dimensione effettiva dell'oggetto: è la dimensione di puntatori e riferimenti. L'enum è un oggetto e le sue dimensioni sono necessarie per consentire al compilatore di accedere alla memoria corretta.
KJA Wolf,

17
Logicamente sarebbe in grado di dichiarare puntatori / riferimenti alle enumerazioni se avessimo enumerazioni anticipate, proprio come possiamo fare con le classi. È solo che spesso non hai a che fare con puntatori agli enum :)
Pavel Minaev,

20
So che questa discussione è finita molto tempo fa, ma devo allinearmi con @j_random_hacker qui: il problema qui non riguarda il puntatore o il riferimento a tipi incompleti, ma sull'uso di tipi incompleti nelle dichiarazioni. Dal momento che è legale farlo struct S; void foo(S s);(si noti che fooè solo dichiarato, non definito), quindi non c'è motivo per cui non si possa fare enum E; void foo(E e);altrettanto. In entrambi i casi, la dimensione non è necessaria.
Luc Touraille,

201

La dichiarazione in avanti di enum è possibile dal C ++ 11. In precedenza, il motivo per cui i tipi di enum non potevano essere dichiarati in avanti è perché la dimensione dell'enumerazione dipende dal suo contenuto. Finché la dimensione dell'enumerazione è specificata dall'applicazione, può essere dichiarata in avanti:

enum Enum1;                   //Illegal in C++ and C++0x; no size is explicitly specified.
enum Enum2 : unsigned int;    //Legal in C++0x.
enum class Enum3;             //Legal in C++0x, because enum class declarations have a default type of "int".
enum class Enum4: unsigned int; //Legal C++0x.
enum Enum2 : unsigned short;  //Illegal in C++0x, because Enum2 was previously declared with a different type.

1
Esiste un supporto compilatore per questa funzione? GCC 4.5 non sembra averlo :(
rubenvb,

4

Stavo cercando enum32_t e con la tua risposta enum XXX: uint32_t {a, b, c};
fantastico

Pensavo che gli enum con ambito (classe enum) fossero implementati in C ++ 11? In tal caso, come sono legali in C ++ 0X?
Terrabits,

1
C ++ 0x era il nome operativo di C ++ 11, @Terrabits, prima che fosse ufficialmente standardizzato. La logica è che se una funzione è nota (o altamente probabile) per essere inclusa in uno standard aggiornato, l'utilizzo di tale funzionalità prima che lo standard venga rilasciato ufficialmente tende a utilizzare il nome di lavoro. (Ad esempio, i compilatori che supportavano le funzionalità C ++ 11 prima della standardizzazione ufficiale nel 2011 avevano il supporto C ++ 0x, i compilatori che supportavano le funzionalità C ++ 17 prima della standardizzazione ufficiale avevano il supporto C ++ 1z e compilatori che supportano le funzionalità C ++ 20 in questo momento (2019) ha il supporto C ++ 2a.)
Justin Time - Ripristina Monica il

79

Sto aggiungendo una risposta aggiornata qui, dati i recenti sviluppi.

È possibile dichiarare in avanti un'enum in C ++ 11, purché si dichiari contemporaneamente il suo tipo di archiviazione. La sintassi è simile al seguente:

enum E : short;
void foo(E e);

....

enum E : short
{
    VALUE_1,
    VALUE_2,
    ....
}

In effetti, se la funzione non fa mai riferimento ai valori dell'enumerazione, in quel momento non è necessaria la dichiarazione completa.

Questo è supportato da G ++ 4.6 e versioni successive ( -std=c++0xo -std=c++11nelle versioni più recenti). Visual C ++ 2013 supporta questo; nelle versioni precedenti ha una sorta di supporto non standard che non ho ancora capito - ho trovato qualche suggerimento che una semplice dichiarazione in avanti è legale, ma YMMV.


4
+1 perché questa è l'unica risposta che menziona la necessità di dichiarare il tipo nella dichiarazione e la definizione.
turoni,

Credo che il supporto parziale nei primi anni di MSVC sia stato backportato da C ++ / CLI enum classcome estensione C ++ (prima che C ++ 11 fosse diverso enum class), almeno se ricordo bene. Il compilatore ti ha permesso di specificare il tipo sottostante di un enum, ma non ha supportato enum classo enum dichiarati in avanti e ti ha avvertito che qualificare un enumeratore con l'ambito dell'enum era un'estensione non standard. Ricordo che funzionava più o meno allo stesso modo di specificare il tipo sottostante in C ++ 11, tranne che più fastidioso perché hai dovuto sopprimere l'avviso.
Justin Time - Ripristina Monica il

30

Dichiarare in avanti le cose in C ++ è molto utile perché accelera notevolmente i tempi di compilazione . È ora possibile dichiarare molte cose in C ++, tra cui: struct, class, function, ecc ...

Ma puoi inoltrare una dichiarazione enumin C ++?

No non puoi.

Ma perché non permetterlo? Se fosse consentito, è possibile definire il enumtipo nel file di intestazione e i enumvalori nel file di origine. Sembra che dovrebbe essere permesso giusto?

Sbagliato.

In C ++ non esiste un tipo predefinito per enumcome in C # (int). In C ++ enumil compilatore determinerà che il tuo tipo sarà qualsiasi tipo adatto all'intervallo di valori che hai per il tuo enum.

Cosa significa?

Significa che il enumtipo di base del tuo non può essere completamente determinato finché non hai tutti i valori del enumdefinito. Quale mans non puoi separare la dichiarazione e la definizione del tuo enum. E pertanto non è possibile inoltrare una dichiarazione enumin C ++.

Lo standard ISO C ++ S7.2.5:

Il tipo sottostante di un'enumerazione è un tipo integrale che può rappresentare tutti i valori dell'enumeratore definiti nell'enumerazione. È definito dall'implementazione quale tipo integrale viene utilizzato come tipo sottostante per un'enumerazione, tranne per il fatto che il tipo sottostante non deve essere maggiore di a intmeno che il valore di un enumeratore non possa rientrare in un into unsigned int. Se l'elenco di enumeratori è vuoto, il tipo sottostante è come se l'enumerazione avesse un singolo enumeratore con valore 0. Il valore di sizeof()applicato a un tipo di enumerazione, un oggetto di tipo di enumerazione o un enumeratore, è il valore di sizeof()applicato al tipo sottostante.

È possibile determinare la dimensione di un tipo enumerato in C ++ utilizzando l' sizeofoperatore. La dimensione del tipo elencato è la dimensione del tipo sottostante. In questo modo puoi indovinare quale tipo sta usando il tuo compilatore enum.

Che cosa succede se si specifica il tipo di enumesplicitamente come questo:

enum Color : char { Red=0, Green=1, Blue=2};
assert(sizeof Color == 1);

Puoi quindi inoltrare il tuo enum?

No. Ma perché no?

La specifica del tipo di an enumnon fa effettivamente parte dell'attuale standard C ++. È un'estensione VC ++. Farà comunque parte di C ++ 0x.

fonte


15
Questa risposta è ormai obsoleta da diversi anni.
Tom,

Il tempo ci prende in giro tutti. Il tuo commento è ormai obsoleto da diversi anni; la risposta un decennio!
pjcard,

14

[La mia risposta è sbagliata, ma l'ho lasciata qui perché i commenti sono utili].

Dichiarare in avanti enum non è standard, perché non è garantito che i puntatori a diversi tipi di enum abbiano le stesse dimensioni. Potrebbe essere necessario che il compilatore visualizzi la definizione per sapere quali puntatori di dimensione possono essere utilizzati con questo tipo.

In pratica, almeno su tutti i compilatori popolari, i puntatori agli enumeratori hanno dimensioni coerenti. Visual C ++, ad esempio, la dichiarazione forward di enum viene fornita come estensione del linguaggio.


2
-1. Se il tuo ragionamento era corretto, lo stesso ragionamento implicherebbe che le dichiarazioni a termine dei tipi di classe non potevano essere utilizzate per creare puntatori a quei tipi, ma possono farlo.
j_random_hacker,

6
+1. Il ragionamento è corretto. Il caso specifico sono le piattaforme in cui sizeof (char *)> sizeof (int *). Entrambi possono essere tipi sottostanti per un enum, a seconda dell'intervallo. Le classi non hanno tipi sottostanti, quindi l'analogia è falsa.
Salterio il

3
@MSalters: Esempio: "struct S {int x;};" Ora, sizeof (S *) deve essere uguale alla dimensione di qualsiasi altro puntatore a struttura, poiché C ++ consente a tale puntatore di essere dichiarato e utilizzato prima della definizione di S ...
j_random_hacker

1
@MSalters: ... Su una piattaforma in cui sizeof (char *)> sizeof (int *), l'utilizzo di un puntatore "full-size" per questa particolare struttura può essere inefficiente, ma semplifica notevolmente la codifica - ed è esattamente lo stesso cosa potrebbe e dovrebbe essere fatta per i tipi di enum.
j_random_hacker

4
i puntatori ai dati e i puntatori alle funzioni possono avere dimensioni diverse, ma sono abbastanza sicuro che i puntatori di dati debbano andare di andata e ritorno (il cast su un altro tipo di puntatore di dati, quindi di nuovo sull'originale, deve ancora funzionare), il che implica che tutti i puntatori ai dati hanno le stesse dimensioni.
Ben Voigt,

7

In effetti non esiste una dichiarazione di enum diretta. Poiché la definizione di un enum non contiene alcun codice che potrebbe dipendere da un altro codice che utilizza l'enum, di solito non è un problema definire completamente l'enum quando lo si dichiara per la prima volta.

Se l'unico uso del tuo enum è da parte di funzioni di membro privato, puoi implementare l'incapsulamento avendo l'enum stesso come membro privato di quella classe. L'enum deve ancora essere completamente definito nel punto di dichiarazione, cioè all'interno della definizione della classe. Tuttavia, questo non è un problema più grande in quanto dichiarando lì le funzioni di membri privati, e non è una peggiore esposizione degli interni di implementazione di quella.

Se hai bisogno di un grado di occultamento più profondo per i dettagli dell'implementazione, puoi suddividerlo in un'interfaccia astratta, composta solo da funzioni virtuali pure e una classe concreta, completamente nascosta, che implementa (eredita) l'interfaccia. La creazione di istanze di classe può essere gestita da una factory o da una funzione membro statica dell'interfaccia. In questo modo, anche il vero nome della classe, per non parlare delle sue funzioni private, non sarà esposto.


5

Solo notando che la ragione in realtà è che la dimensione dell'enum non è ancora nota dopo la dichiarazione in avanti. Bene, usi la dichiarazione diretta di una struttura per essere in grado di passare un puntatore o fare riferimento a un oggetto da un posto a cui fa riferimento anche la stessa definizione della struttura dichiarata diretta.

Dichiarare in avanti un enum non sarebbe troppo utile, perché si vorrebbe poter passare l'enum per valore. Non potresti nemmeno avere un puntatore ad esso, perché recentemente mi è stato detto che alcune piattaforme usano puntatori di dimensioni diverse per char che per int o long. Quindi tutto dipende dal contenuto dell'enum.

L'attuale standard C ++ non consente esplicitamente di fare qualcosa del genere

enum X;

(in 7.1.5.3/1). Ma il prossimo standard C ++ dovuto al prossimo anno consente quanto segue, il che mi ha convinto che il problema abbia effettivamente a che fare con il tipo sottostante:

enum X : int;

È conosciuta come una dichiarazione enum "opaca". Puoi anche usare X per valore nel seguente codice. E i suoi enumeratori possono essere successivamente definiti in una successiva redeclaration dell'enumerazione. Vedi 7.2nella bozza di lavoro corrente.


4

Lo farei così:

[nell'intestazione pubblica]

typedef unsigned long E;

void Foo(E e);

[nell'intestazione interna]

enum Econtent { FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X,
  FORCE_32BIT = 0xFFFFFFFF };

Aggiungendo FORCE_32BIT ci assicuriamo che Econtent si compili in un lungo, quindi è intercambiabile con E.


1
Ovviamente, ciò significa che (A) i tipi di E ed Econtent differiscono e (B) sui sistemi LP64, dimensione di (E) = 2 * dimensione di (EContent). Trivial fix: ULONG_MAX, anche più facile da leggere.
MSalters,

2

Se davvero non vuoi che il tuo enum appaia nel tuo file header e assicuri che sia usato solo da metodi privati, una soluzione può essere quella di seguire il principio pimpl.

È una tecnica che assicura di nascondere gli interni della classe nelle intestazioni dichiarando semplicemente:

class A 
{
public:
    ...
private:
    void* pImpl;
};

Quindi nel tuo file di implementazione (cpp), dichiari una classe che sarà la rappresentazione degli interni.

class AImpl
{
public:
    AImpl(A* pThis): m_pThis(pThis) {}

    ... all private methods here ...
private:
    A* m_pThis;
};

È necessario creare in modo dinamico l'implementazione nel costruttore della classe ed eliminarla nel distruttore e quando si implementa il metodo pubblico, è necessario utilizzare:

((AImpl*)pImpl)->PrivateMethod();

Esistono dei vantaggi nell'uso di pimpl, uno è che disaccoppia l'intestazione della sua classe dalla sua implementazione, non è necessario ricompilare altre classi quando si cambia un'implementazione di una classe. Un altro è che accelera i tempi di compilazione perché le intestazioni sono così semplici.

Ma è un dolore da usare, quindi dovresti davvero chiederti se dichiarare che il tuo enum come privato nell'intestazione è un vero problema.


3
struct AImpl; struct A {privato: AImpl * pImpl; };

2

È possibile racchiudere l'enum in una struttura, aggiungere alcuni costruttori e digitare conversioni e inoltrare invece la struttura.

#define ENUM_CLASS(NAME, TYPE, VALUES...) \
struct NAME { \
    enum e { VALUES }; \
    explicit NAME(TYPE v) : val(v) {} \
    NAME(e v) : val(v) {} \
    operator e() const { return e(val); } \
    private:\
        TYPE val; \
}

Questo sembra funzionare: http://ideone.com/TYtP2


1

Sembra che non possa essere dichiarato in avanti in GCC!

Discussione interessante qui


1

C'è un po 'di dissenso da quando questo è stato urtato (una specie di), quindi ecco alcuni bit rilevanti dello standard. La ricerca mostra che lo standard non definisce realmente la dichiarazione a termine, né afferma esplicitamente che gli enum possono o non possono essere dichiarati a termine.

Innanzitutto, da dcl.enum, sezione 7.2:

Il tipo sottostante di un'enumerazione è un tipo integrale che può rappresentare tutti i valori dell'enumeratore definiti nell'enumerazione. Viene definito l'implementazione quale tipo integrale viene utilizzato come tipo sottostante per un'enumerazione, tranne per il fatto che il tipo sottostante non deve essere maggiore di int a meno che il valore di un enumeratore non possa rientrare in un int o in un unsigned int. Se l'elenco di enumeratori è vuoto, il tipo sottostante è come se l'enumerazione avesse un singolo enumeratore con valore 0. Il valore di sizeof () applicato a un tipo di enumerazione, un oggetto di tipo di enumerazione o un enumeratore, è il valore di sizeof () applicato al tipo sottostante.

Quindi il tipo sottostante di un enum è definito dall'implementazione, con una limitazione minore.

Ora passiamo alla sezione sui "tipi incompleti" (3.9), che è più o meno vicina a qualsiasi standard sulle dichiarazioni a termine:

Una classe che è stata dichiarata ma non definita, o una matrice di dimensione sconosciuta o di tipo di elemento incompleto, è un tipo di oggetto definito in modo incompleto.

Un tipo di classe (come "classe X") potrebbe essere incompleto in un punto in un'unità di traduzione e completare in seguito; il tipo "classe X" è lo stesso tipo in entrambi i punti. Il tipo dichiarato di un oggetto array può essere un array di tipo di classe incompleto e quindi incompleto; se il tipo di classe viene completato successivamente nell'unità di traduzione, il tipo di matrice diventa completo; il tipo di array in questi due punti è dello stesso tipo. Il tipo dichiarato di un oggetto array può essere un array di dimensioni sconosciute e quindi essere incompleto in un punto in un'unità di traduzione e completare in seguito; i tipi di array in questi due punti ("array di limite sconosciuto di T" e "array di N T") sono tipi diversi. Il tipo di un puntatore a un array di dimensioni sconosciute o di un tipo definito da una dichiarazione typedef come un array di dimensioni sconosciute,

Quindi, lo standard ha praticamente definito i tipi che possono essere dichiarati in avanti. Enum non c'era, quindi gli autori di compilatori generalmente considerano la dichiarazione anticipata non consentita dallo standard a causa delle dimensioni variabili del tipo sottostante.

Ha anche senso. In genere, gli enum sono referenziati in situazioni per valore, e il compilatore dovrebbe effettivamente conoscere la dimensione della memoria in quelle situazioni. Poiché la dimensione della memoria è definita dall'implementazione, molti compilatori possono semplicemente scegliere di utilizzare valori a 32 bit per il tipo sottostante di ogni enum, a quel punto diventa possibile inoltrarli dichiarandoli. Un esperimento interessante potrebbe essere quello di provare a dichiarare un enum in Visual Studio, costringendolo quindi a usare un tipo sottostante maggiore di sizeof (int) come spiegato sopra per vedere cosa succede.


si noti che non consente esplicitamente "enum foo;" in 7.1.5.3/1 (ma come per tutto, fintanto che il compilatore avverte, potrebbe comunque compilare tale codice, ovviamente)
Johannes Schaub - litb

Grazie per averlo sottolineato, è un paragrafo davvero esoterico e potrebbe volerci una settimana per analizzarlo. Ma è bello sapere che è lì.
Dan Olson,

nessun problema, alcuni paragrafi standard sono davvero strani :) beh, un elaboratore di tipo è uno strumento in cui si specifica un tipo, ma si specifica anche qualcosa in più per renderlo inequivocabile. ad es. "struct X" invece di "X", o "enum Y" anziché solo "Y". Ne hai bisogno per affermare che qualcosa è davvero un tipo.
Johannes Schaub - litb

così puoi usarlo in questo modo: "class X * foo;" se X non fosse stato ancora dichiarato in avanti. o "typename X :: foo" in un modello per chiarimento delle ambiguità. o "class link obj;" se esiste una funzione "link" nello stesso ambito che oscurerebbe la classe con lo stesso nome.
Johannes Schaub -

in 3.4.4 dice che sono usati se qualche nome non di tipo nasconde un nome di tipo. è lì che vengono usati più spesso, oltre a dichiarare in avanti come "classe X;" (qui è l'unica forma di una dichiarazione). ne parla qui in non template. tuttavia, 14.6 / 3 ne elenca un uso nei template.
Johannes Schaub -

1

Per VC, ecco il test sulla dichiarazione diretta e sulla specifica del tipo sottostante:

  1. il seguente codice è compilato ok.
    typedef int myint;
    enum T;
    void foo (T * tp)
    {
        * tp = (T) 0x12345678;
    }
    enum T: char
    {
        UN
    };

Ma ho ricevuto l'avviso per / W4 (/ W3 non ha questo avviso)

avviso C4480: estensione non standard utilizzata: specifica del tipo sottostante per enum 'T'

  1. VC (Microsoft (R) 32-bit C / C ++ Ottimizzazione versione del compilatore 15.00.30729.01 per 80x86) sembra difettoso nel caso precedente:

    • quando si vede enum T; VC presuppone che il tipo enum T utilizzi 4 byte predefiniti come tipo sottostante, quindi il codice assembly generato è:
    ? foo @@ YAXPAW4T @@@ Z PROC; foo
    ; File e: \ work \ c_cpp \ cpp_snippet.cpp
    ; Linea 13
        spingere ebp
        mov ebp, esp
    ; Linea 14
        mov eax, DWORD PTR _tp $ [ebp]
        mov DWORD PTR [eax], 305419896; 12345678H
    ; Linea 15
        pop ebp
        ret 0
    ? foo @@ YAXPAW4T @@@ Z ENDP; foo

Il codice assembly sopra riportato viene estratto direttamente da /Festest.asm, non è una mia ipotesi personale. Vedi il mov DWORD PTR [eax], 305419896; Linea 12345678H?

il seguente frammento di codice lo dimostra:

    int main (int argc, char * argv)
    {
        unione {
            char ca [4];
            T t;
        }un;
        a.ca [0] = a.ca [1] = a. [ca [2] = a.ca [3] = 1;
        pippo (& a.t);
        printf ("% # x,% # x,% # x,% # x \ n", a.ca [0], a.ca [1], a.ca [2], a.ca [3]) ;
        ritorna 0;
    }

il risultato è: 0x78, 0x56, 0x34, 0x12

  • dopo rimuovere la dichiarazione in avanti di enum T e spostare la definizione di funzione foo dopo la definizione di enum T: il risultato è OK:

le istruzioni chiave sopra diventano:

mov BYTE PTR [eax], 120; 00000078H

il risultato finale è: 0x78, 0x1, 0x1, 0x1

Si noti che il valore non viene sovrascritto

Quindi l'uso della dichiarazione anticipata di enum in VC è considerato dannoso.

A proposito, per non sorprendere, la sintassi per la dichiarazione del tipo sottostante è la stessa in C #. In pratica ho scoperto che vale la pena salvare 3 byte specificando il tipo sottostante come char quando si parla con il sistema incorporato, che è limitato dalla memoria.


1

Nei miei progetti, ho adottato la tecnica di enumerazione legata allo spazio dei nomi per gestire enumi componenti legacy e di terze parti. Ecco un esempio:

forward.h:

namespace type
{
    class legacy_type;
    typedef const legacy_type& type;
}

enum.h:

// May be defined here or pulled in via #include.
namespace legacy
{
    enum evil { x , y, z };
}


namespace type
{
    using legacy::evil;

    class legacy_type
    {
    public:
        legacy_type(evil e)
            : e_(e)
        {}

        operator evil() const
        {
            return e_;
        }

    private:
        evil e_;
    };
}

foo.h:

#include "forward.h"

class foo
{
public:
    void f(type::type t);
};

foo.cc:

#include "foo.h"

#include <iostream>
#include "enum.h"

void foo::f(type::type t)
{
    switch (t)
    {
        case legacy::x:
            std::cout << "x" << std::endl;
            break;
        case legacy::y:
            std::cout << "y" << std::endl;
            break;
        case legacy::z:
            std::cout << "z" << std::endl;
            break;
        default:
            std::cout << "default" << std::endl;
    }
}

main.cc:

#include "foo.h"
#include "enum.h"

int main()
{
    foo fu;
    fu.f(legacy::x);

    return 0;
}

Nota che l' foo.hintestazione non deve sapere nulla legacy::evil. Solo i file che utilizzano il tipo legacy legacy::evil(qui: main.cc) devono essere inclusi enum.h.


0

La mia soluzione al tuo problema sarebbe di:

1 - usa int invece di enums: dichiara i tuoi ints in uno spazio dei nomi anonimo nel tuo file CPP (non nell'intestazione):

namespace
{
   const int FUNCTIONALITY_NORMAL = 0 ;
   const int FUNCTIONALITY_RESTRICTED = 1 ;
   const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
}

Dato che i tuoi metodi sono privati, nessuno rovinerà i dati. Potresti anche andare oltre per testare se qualcuno ti invia dati non validi:

namespace
{
   const int FUNCTIONALITY_begin = 0 ;
   const int FUNCTIONALITY_NORMAL = 0 ;
   const int FUNCTIONALITY_RESTRICTED = 1 ;
   const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
   const int FUNCTIONALITY_end = 3 ;

   bool isFunctionalityCorrect(int i)
   {
      return (i >= FUNCTIONALITY_begin) && (i < FUNCTIONALITY_end) ;
   }
}

2: crea una classe completa con istanze const limitate, come fatto in Java. Inoltra dichiarare la classe, quindi definirla nel file CPP e istanziare solo i valori simili a enum. Ho fatto qualcosa del genere in C ++, e il risultato non è stato soddisfacente come desiderato, poiché aveva bisogno di un po 'di codice per simulare un enum (costruzione di copia, operatore =, ecc.).

3: Come proposto in precedenza, utilizzare l'enum dichiarato privatamente. Nonostante il fatto che un utente vedrà la sua definizione completa, non sarà in grado di usarlo, né utilizzare i metodi privati. Quindi di solito sarai in grado di modificare l'enum e il contenuto dei metodi esistenti senza bisogno di ricompilare il codice usando la tua classe.

La mia ipotesi sarebbe la soluzione 3 o 1.


-1

Poiché l'enum può essere una dimensione integrale di dimensioni variabili (il compilatore decide quale dimensione ha un determinato enum), anche il puntatore all'enum può avere dimensioni variabili, poiché è un tipo integrale (i caratteri hanno puntatori di dimensioni diverse su alcune piattaforme per esempio).

Quindi il compilatore non può nemmeno permetterti di dichiarare in avanti l'enum e l'utente un puntatore ad esso, perché anche lì ha bisogno della dimensione dell'enum.


-1

Si definisce un'enumerazione per limitare i possibili valori degli elementi del tipo a un set limitato. Questa limitazione deve essere applicata al momento della compilazione.

Quando si dichiara in avanti il ​​fatto che in seguito verrà utilizzato un "set limitato" non si aggiunge alcun valore: il codice successivo deve conoscere i possibili valori per poterne beneficiare.

Sebbene il compilatore sia preoccupato per le dimensioni del tipo enumerato, l' intento dell'enumerazione si perde quando lo si inoltra dichiarandolo.


1
No, il codice successivo non ha bisogno di conoscere i valori per questo utile - in particolare, se il codice successivo è semplicemente un prototipo di funzione che accetta o restituisce parametri enum, la dimensione del tipo non è importante. L'uso della dichiarazione diretta qui può rimuovere le dipendenze di compilazione, accelerando la compilazione.
j_random_hacker,

Hai ragione. L'intento non è obbedire ai valori, ma al tipo. Risolto con tipi di enum 0x.
xtofl
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.