Un enum X : int
(C #) o enum class X : int
(C ++ 11) è un tipo che ha un campo interno nascosto int
che può contenere qualsiasi valore. Inoltre, un numero di costanti predefinite di X
sono definite sull'enum. È possibile eseguire il cast dell'enum al suo valore intero e viceversa. Questo è vero sia in C # che in C ++ 11.
In C # gli enumeni non sono usati solo per contenere singoli valori, ma anche per contenere combinazioni bit per bit di flag, come da raccomandazione di Microsoft . Tali enum sono (di solito, ma non necessariamente) decorati con l' [Flags]
attributo. Per semplificare la vita degli sviluppatori, gli operatori bit a bit (OR, AND, ecc ...) sono sovraccarichi in modo da poter facilmente fare qualcosa del genere (C #):
void M(NumericType flags);
M(NumericType.Sign | NumericType.ZeroPadding);
Sono uno sviluppatore esperto di C #, ma sto programmando C ++ solo da un paio di giorni e non sono noto con le convenzioni C ++. Ho intenzione di usare un enum C ++ 11 nello stesso modo in cui ero abituato a fare in C #. In C ++ 11 gli operatori bit a bit sugli enum con ambito non sono sovraccarichi, quindi volevo sovraccaricarli .
Ciò ha sollecitato un dibattito e le opinioni sembrano variare tra tre opzioni:
Una variabile di tipo enum viene utilizzata per contenere il campo bit, simile a C #:
void M(NumericType flags); // With operator overloading: M(NumericType::Sign | NumericType::ZeroPadding); // Without operator overloading: M(static_cast<NumericType>(static_cast<int>(NumericType::Sign) | static_cast<int>(NumericType::ZeroPadding)));
Ma questo contrasterebbe la filosofia enum fortemente tipizzata degli enumerati con scope C ++ 11.
Utilizzare un numero intero semplice se si desidera memorizzare una combinazione bit a bit di enum:
void M(int flags); M(static_cast<int>(NumericType::Sign) | static_cast<int>(NumericType::ZeroPadding));
Ma questo ridurrebbe tutto a un
int
, lasciandoti senza la minima idea di quale tipo dovresti inserire nel metodo.Scrivi una classe separata che sovraccaricherà gli operatori e manterrà i flag bit a bit in un campo intero nascosto:
class NumericTypeFlags { unsigned flags_; public: NumericTypeFlags () : flags_(0) {} NumericTypeFlags (NumericType t) : flags_(static_cast<unsigned>(t)) {} //...define BITWISE test/set operations }; void M(NumericTypeFlags flags); M(NumericType::Sign | NumericType::ZeroPadding);
( Codice completo da user315052 )
Ma allora non hai IntelliSense o qualunque supporto per suggerirti i possibili valori.
So che questa è una domanda soggettiva , ma: quale approccio dovrei usare? Quale approccio, se esiste, è il più ampiamente riconosciuto in C ++? Quale approccio usi quando gestisci i campi di bit e perché ?
Ovviamente, poiché tutti e tre gli approcci funzionano, sto cercando ragioni concrete e tecniche, convenzioni generalmente accettate e non semplicemente preferenze personali.
Ad esempio, a causa del mio background in C #, tendo ad andare con l'approccio 1 in C ++. Questo ha l'ulteriore vantaggio che il mio ambiente di sviluppo può suggerirmi i possibili valori, e con operatori enum sovraccarichi questo è facile da scrivere e capire e abbastanza pulito. E la firma del metodo mostra chiaramente quale tipo di valore si aspetta. Ma la maggior parte delle persone qui non è d'accordo con me, probabilmente per una buona ragione.
enum E { A = 1, B = 2, C = 4, };
, l'intervallo è 0..7
(3 bit). Pertanto, lo standard C ++ garantisce esplicitamente che il numero 1 sarà sempre un'opzione praticabile. [In particolare, il enum class
valore predefinito è enum class : int
se non diversamente specificato, e quindi ha sempre un tipo sottostante fisso.])