Das Behandeln von enum
s als Flags funktioniert in C # über das [Flags]
Attribut gut, aber wie geht das in C ++ am besten?
Zum Beispiel möchte ich schreiben:
enum AnimalFlags
{
HasClaws = 1,
CanFly =2,
EatsFish = 4,
Endangered = 8
};
seahawk.flags = CanFly | EatsFish | Endangered;
Ich erhalte jedoch Compilerfehler bezüglich int
/ enum
Konvertierungen. Gibt es eine schönere Möglichkeit, dies auszudrücken, als nur stumpfes Casting? Vorzugsweise möchte ich mich nicht auf Konstrukte aus Bibliotheken von Drittanbietern wie Boost oder Qt verlassen.
EDIT: Wie in den Antworten angegeben, kann ich den Compilerfehler vermeiden, indem ich seahawk.flags
als deklariere int
. Ich hätte jedoch gerne einen Mechanismus, um die Typensicherheit zu erzwingen, damit jemand nicht schreiben kann seahawk.flags = HasMaximizeButton
.
[Flags]
, funktioniert das Attribut[Flags] enum class FlagBits{ Ready = 1, ReadMode = 2, WriteMode = 4, EOF = 8, Disabled = 16};
Antworten:
Der "richtige" Weg besteht darin, Bitoperatoren für die Aufzählung wie folgt zu definieren:
Usw. Rest der Bitoperatoren. Ändern Sie nach Bedarf, wenn der Enum-Bereich den Int-Bereich überschreitet.
quelle
AnimalFlags
wird durch den Ausdruck dargestelltHasClaws | CanFly
? Dafür sind wir nicht daenum
. Verwenden Sie Ganzzahlen und Konstanten.(HasClaws | CanFly)
".HasClaws
(= 1) als auchCanFly
(= 2). Wenn Sie stattdessen nur die Werte 1 bis 4 direkt zuweisen und eine 3 erhalten, kann es sich um eine einzelneEatsFish
oder eine Kombination ausHasClaws
und handelnCanFly
. Wenn Ihre Aufzählung nur exklusive Zustände angibt, sind aufeinanderfolgende Werte in Ordnung, aber für eine Kombination von Flags müssen die Werte bitexklusiv sein.Hinweis (auch etwas abseits des Themas): Eine andere Möglichkeit, eindeutige Flags zu erstellen, kann durch eine Bitverschiebung erfolgen. Ich selbst finde das leichter zu lesen.
Es kann Werte bis zu einem int halten, dh meistens 32 Flags, was sich deutlich im Verschiebungsbetrag widerspiegelt.
quelle
Für faule Leute wie mich gibt es hier eine Lösung zum Kopieren und Einfügen:
quelle
using
es gegebenenfalls genau sorel_ops
.Hinweis: Wenn Sie in einer Windows-Umgebung arbeiten, ist
DEFINE_ENUM_FLAG_OPERATORS
in winnt.h ein Makro definiert, das die Aufgabe für Sie erledigt. In diesem Fall können Sie Folgendes tun:quelle
Welcher Typ ist die Variable seahawk.flags?
In Standard-C ++ sind Aufzählungen nicht typsicher. Sie sind effektiv ganze Zahlen.
AnimalFlags sollten NICHT der Typ Ihrer Variablen sein. Ihre Variable sollte int sein und der Fehler wird verschwinden.
Das Setzen von Hexadezimalwerten, wie von einigen anderen Personen vorgeschlagen, ist nicht erforderlich. Es macht keinen Unterschied.
Die Aufzählungswerte sind standardmäßig vom Typ int. Sie können sie also sicher bitweise ODER kombinieren und zusammenfügen und das Ergebnis in einem Int speichern.
Der Aufzählungstyp ist eine eingeschränkte Teilmenge von int, deren Wert einer der aufgezählten Werte ist. Wenn Sie also einen neuen Wert außerhalb dieses Bereichs erstellen, können Sie ihn nicht zuweisen, ohne eine Variable Ihres Aufzählungstyps umzuwandeln.
Sie können auch die Aufzählungswerttypen ändern, wenn Sie möchten, aber diese Frage hat keinen Sinn.
BEARBEITEN: Auf dem Poster stand, dass sie sich mit der befassen und keinen Wert möchten, der innerhalb des int-Typs nicht vorhanden sein sollte.
Es wäre jedoch vom Typ unsicher, einen Wert außerhalb des Bereichs von AnimalFlags in eine Variable vom Typ AnimalFlags einzufügen.
Es gibt eine sichere Möglichkeit, innerhalb des int-Typs nach Werten außerhalb des Bereichs zu suchen ...
Das Obige hindert Sie nicht daran, ein ungültiges Flag aus einer anderen Aufzählung mit dem Wert 1,2,4 oder 8 zu setzen.
Wenn Sie absolute Typensicherheit wünschen, können Sie einfach ein std :: set erstellen und jedes Flag dort speichern. Es ist nicht platzsparend, aber typsicher und bietet Ihnen die gleichen Fähigkeiten wie ein Bitflag-Int.
C ++ 0x Hinweis: Stark typisierte Aufzählungen
In C ++ 0x können Sie endlich typsichere Aufzählungswerte haben ....
quelle
HasClaws | CanFly
ist ein ganzzahliger Typ, aber der Typ vonHasClaws
istAnimalFlags
kein ganzzahliger Typ.max(|emin| − K, |emax|)
und gleich(1u<<M) - 1
, wobeiM
es sich um eine nicht negative ganze Zahl handelt. "int
.enum
technisch nicht standardmäßig aufint
als die ihr zugrunde liegenden Typ (entweder Pre-C ++ 11 (IIRC) oder Post-C ++ 11 , wenn keine zugrunde liegende Typ angegeben ist), obwohlenum class
tut . Stattdessen verwendet der zugrunde liegende Typ standardmäßig etwas, das groß genug ist, um alle Enumeratoren darzustellen, mit der einzigen wirklich harten Regel, dass er nur größer ist, alsint
wenn es explizit sein muss . Grundsätzlich wird der zugrunde liegende Typ als (umschrieben) "was auch immer funktioniert, aber es ist wahrscheinlich, esint
sei denn, die Enumeratoren sind zu groß fürint
" angegeben.Ich finde die derzeit akzeptierte Antwort von Eidolon zu gefährlich. Das Optimierungsprogramm des Compilers kann Annahmen über mögliche Werte in der Aufzählung treffen und Sie erhalten möglicherweise Müll mit ungültigen Werten zurück. Und normalerweise möchte niemand alle möglichen Permutationen in Flags-Enums definieren.
Wie Brian R. Bondy weiter unten ausführt, können Sie dies jetzt einfacher tun, wenn Sie C ++ 11 verwenden (was jeder sollte, es ist so gut)
enum class
:Dies stellt einen stabilen Größen- und Wertebereich sicher, indem ein Typ für die Aufzählung angegeben wird, verhindert das automatische Downcasting von Aufzählungen auf Ints usw. durch Verwendung
enum class
und stelltconstexpr
sicher, dass der Code für die Operatoren inline und damit genauso schnell wie reguläre Zahlen wird.Für Leute, die mit C ++ - Dialekten vor 11 Jahren zu tun haben
Wenn ich mit einem Compiler feststecken würde, der C ++ 11 nicht unterstützt, würde ich einen int-Typ in eine Klasse einschließen, die dann nur die Verwendung bitweiser Operatoren und der Typen aus dieser Aufzählung erlaubt, um ihre Werte festzulegen:
Sie können dies so ziemlich wie eine reguläre Aufzählung + typedef definieren:
Und die Verwendung ist ähnlich:
Sie können den zugrunde liegenden Typ auch für binär stabile Aufzählungen (wie C ++ 11
enum foo : type
) mithilfe des zweiten Vorlagenparameters überschreiben , dtypedef SafeEnum<enum TFlags_,uint8_t> TFlags;
. H.Ich habe die
operator bool
Überschreibung mit demexplicit
Schlüsselwort C ++ 11 markiert , um zu verhindern, dass sie zu int-Konvertierungen führt, da diese dazu führen können, dass Flagsets beim Ausschreiben auf 0 oder 1 reduziert werden. Wenn Sie C ++ 11 nicht verwenden können, lassen Sie diese Überladung weg und schreiben Sie die erste Bedingung in der Beispielverwendung als neu(myFlags & EFlagTwo) == EFlagTwo
.quelle
std::underlying_type
einen bestimmten Typ verwendet, anstatt ihn fest zu codieren, oder dass der zugrunde liegende Typ bereitgestellt und als Typalias anstelle von direkt verwendet wird. Auf diese Weise werden Änderungen am zugrunde liegenden Typ automatisch weitergegeben, anstatt manuell vorgenommen werden zu müssen.Der einfachste Weg , dies zu tun , wie gezeigt hier , die Standard - Bibliothek - Klasse bitset .
Um die C # -Funktion typsicher zu emulieren, müssen Sie einen Vorlagen-Wrapper um das Bitset schreiben und die int-Argumente durch eine Aufzählung ersetzen, die der Vorlage als Typparameter zugewiesen wird. Etwas wie:
quelle
Meiner Meinung nach ist keine der bisherigen Antworten ideal. Um ideal zu sein, würde ich die Lösung erwarten:
==
,!=
,=
,&
,&=
,|
,|=
und~
Operatoren im herkömmlichen Sinne (dha & b
)if (a & b)...
Die meisten der bisherigen Lösungen fallen auf Punkt 2 oder 3 um. WebDancer ist meiner Meinung nach der Abschluss, schlägt jedoch bei Punkt 3 fehl und muss für jede Aufzählung wiederholt werden.
Meine vorgeschlagene Lösung ist eine verallgemeinerte Version von WebDancer, die auch Punkt 3 behandelt:
Dies führt zu Überladungen der erforderlichen Operatoren, verwendet jedoch SFINAE, um sie auf aufgezählte Typen zu beschränken. Beachten Sie, dass ich der Kürze halber nicht alle Operatoren definiert habe, aber der einzige, der anders ist, ist der
&
. Die Operatoren sind derzeit global (dh gelten für alle aufgezählten Typen). Dies kann jedoch entweder durch Platzieren der Überladungen in einem Namespace (wie ich es tue) oder durch Hinzufügen zusätzlicher SFINAE-Bedingungen (möglicherweise unter Verwendung bestimmter zugrunde liegender Typen oder speziell erstellter Typaliasnamen) verringert werden ). Dasunderlying_type_t
ist eine C ++ 14-Funktion, aber es scheint gut unterstützt zu werden und ist für C ++ 11 mit einem einfachen einfach zu emulierentemplate<typename T> using underlying_type_t = underlying_type<T>::type;
quelle
Der C ++ - Standard spricht ausdrücklich davon, siehe Abschnitt "17.5.2.1.3 Bitmaskentypen":
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3485.pdf
Mit dieser "Vorlage" erhalten Sie:
Und ähnlich für die anderen Betreiber. Beachten Sie auch den "constexpr", der benötigt wird, wenn der Compiler die Kompilierungszeit des Operators ausführen kann.
Wenn Sie C ++ / CLI verwenden und Enum-Mitgliedern von Ref-Klassen zuweisen möchten, müssen Sie stattdessen Tracking-Referenzen verwenden:
HINWEIS: Dieses Beispiel ist nicht vollständig. Eine vollständige Reihe von Operatoren finden Sie in Abschnitt "17.5.2.1.3 Bitmaskentypen".
quelle
Ich stellte die gleiche Frage und fand eine generische C ++ 11-basierte Lösung, ähnlich der von soru:
Die Oberfläche kann nach Geschmack verbessert werden. Dann kann es so verwendet werden:
quelle
Wenn Ihr Compiler noch keine stark typisierten Aufzählungen unterstützt, können Sie den folgenden Artikel aus der C ++ - Quelle lesen :
Aus der Zusammenfassung:
quelle
Ich benutze das folgende Makro:
Es ähnelt den oben genannten, weist jedoch mehrere Verbesserungen auf:
int
)Es muss type_traits enthalten:
quelle
Ich möchte die Antwort von Uliwitness näher erläutern , seinen Code für C ++ 98 korrigieren und die Safe Bool-Sprache verwenden , da die
std::underlying_type<>
Vorlage und die fehlenexplicit
Schlüsselwort in C ++ - Versionen unter C ++ 11 .Ich habe es auch so modifiziert, dass die Enum-Werte ohne explizite Zuweisung sequentiell sein können
Sie können dann den Raw-Flags-Wert mit abrufen
Hier ist der Code.
quelle
Hier ist eine Option für Bitmasken, wenn Sie die einzelnen Enum-Werte nicht verwenden können (z. B. müssen Sie sie nicht ausschalten) ... und wenn Sie sich keine Sorgen um die Aufrechterhaltung der Binärkompatibilität machen, dh: Sie Es ist mir egal, wo deine Teile leben ... was du wahrscheinlich bist. Außerdem sollten Sie sich nicht zu sehr mit dem Umfang und der Zugriffskontrolle befassen. Hmmm, Enums haben einige nette Eigenschaften für Bitfelder ... frage mich, ob das jemals jemand versucht hat :)
Wir können sehen, dass das Leben großartig ist, wir haben unsere diskreten Werte und wir haben eine nette Beziehung zu & und | nach Herzenslust, der immer noch den Kontext dessen hat, was seine Teile bedeuten. Alles ist konsistent und vorhersehbar ... für mich ... solange ich weiterhin den VC ++ - Compiler von Microsoft mit Update 3 unter Win10 x64 verwende und meine Compiler-Flags nicht berühre :)
Obwohl alles großartig ist ... haben wir jetzt einen gewissen Kontext bezüglich der Bedeutung von Flags, da es sich um eine Vereinigung mit dem Bitfeld in der schrecklichen realen Welt handelt, in der Ihr Programm möglicherweise für mehr als eine einzelne diskrete Aufgabe verantwortlich ist, die Sie könnten immer noch versehentlich (ziemlich leicht) zwei Flaggenfelder verschiedener Gewerkschaften zusammenschlagen (z. B. AnimalProperties und ObjectProperties, da sie beide Ints sind) und all deine Teile durcheinander bringen, was ein schrecklicher Fehler ist, den man aufspüren kann ... und woher ich weiß Viele Leute in diesem Beitrag arbeiten nicht sehr oft mit Bitmasken, da das Erstellen einfach und die Wartung schwierig ist.
Dann machen Sie Ihre Gewerkschaftserklärung privat, um den direkten Zugriff auf "Flags" zu verhindern, und müssen Getter / Setter und Operatorüberladungen hinzufügen, dann ein Makro für all das erstellen, und Sie sind im Grunde wieder da, wo Sie angefangen haben, als Sie es versucht haben Mach das mit einer Aufzählung.
Wenn Sie möchten, dass Ihr Code portabel ist, gibt es leider keine Möglichkeit, entweder A) das Bitlayout zu garantieren oder B) das Bitlayout zur Kompilierungszeit zu bestimmen (damit Sie es verfolgen und zumindest Änderungen korrigieren können) Versionen / Plattformen etc) Offset in einer Struktur mit Bitfeldern
Zur Laufzeit können Sie Streiche spielen, indem Sie die Felder setzen und die Flags XOR-verknüpfen, um zu sehen, welche Bits sich geändert haben. Das klingt für mich ziemlich beschissen, obwohl Verse eine 100% konsistente, plattformunabhängige und vollständig deterministische Lösung haben, dh: eine ENUM.
TL; DR: Hör nicht auf die Hasser. C ++ ist kein Englisch. Nur weil die wörtliche Definition eines von C geerbten abgekürzten Schlüsselworts möglicherweise nicht zu Ihrer Verwendung passt, heißt das nicht, dass Sie es nicht verwenden sollten, wenn C und C ++ - Definition des Schlüsselworts Ihren Anwendungsfall absolut einschließt. Sie können auch Strukturen verwenden, um andere Dinge als Strukturen zu modellieren, und Klassen für andere Dinge als Schule und soziale Kaste. Sie können float für Werte verwenden, die geerdet sind. Sie können char für Variablen verwenden, die weder unverbrannt noch eine Person in einem Roman, Theaterstück oder Film sind. Jeder Programmierer, der zum Wörterbuch geht, um die Bedeutung eines Schlüsselworts vor der Sprachspezifikation zu bestimmen, ist ein ... Nun, ich werde meine Zunge dort halten.
Wenn Sie möchten, dass Ihr Code der gesprochenen Sprache nachempfunden ist, schreiben Sie am besten in Objective-C, das übrigens auch häufig Enums für Bitfelder verwendet.
quelle
Nur syntaktischer Zucker. Keine zusätzlichen Metadaten.
Flag-Operatoren für integrale Typen funktionieren einfach.
quelle
Derzeit gibt es keine Sprachunterstützung für Enum-Flags. Meta-Klassen könnten diese Funktion von Natur aus hinzufügen, falls sie jemals Teil des C ++ - Standards sein sollte.
Meine Lösung wäre, nur enum-instanziierte Vorlagenfunktionen zu erstellen, die typsichere bitweise Operationen für die enum-Klasse unter Verwendung des zugrunde liegenden Typs unterstützen:
Datei: EnumClassBitwise.h
Zur Vereinfachung und zur Reduzierung von Fehlern möchten Sie Ihre Bit-Flags-Operationen möglicherweise auch für Aufzählungen und Ganzzahlen umschließen:
Datei: BitFlags.h
Mögliche Verwendung:
quelle
@Xaqq bietet eine wirklich schöne typsichere Möglichkeit, Enum-Flags hier von a zu verwenden
flag_set
Klasse zu verwenden.Ich habe den Code in GitHub veröffentlicht. Die Verwendung ist wie folgt:
quelle
Sie verwirren Objekte und Sammlungen von Objekten. Insbesondere verwechseln Sie Binärflags mit Sätzen von Binärflags. Eine richtige Lösung würde so aussehen:
quelle
Hier ist meine Lösung, ohne dass eine Menge Überladung oder Casting erforderlich ist:
Ich denke, es ist in Ordnung, weil wir sowieso (nicht stark typisierte) Aufzählungen und Ints identifizieren.
Nur als (längere) Randnotiz, wenn Sie
Ich würde mir das einfallen lassen:
Verwenden von C ++ 11-Initialisiererlisten und
enum class
.quelle
using Flags = unsigned long
innerhalb eines Namespace oder einer Struktur verwenden, das die Flag-Werte selbst als/*static*/ const Flags XY = 0x01
usw. enthält.Wie oben (Kai) oder wie folgt. Wirklich Aufzählungen sind "Aufzählungen". Was Sie tun möchten, ist eine Menge zu haben, daher sollten Sie wirklich stl :: set verwenden
quelle
Vielleicht wie NS_OPTIONS von Objective-C.
quelle