Enum vs Stark getippte Enum

83

Ich bin ein Anfänger in der C ++ - Programmierung.

Heute stoße ich auf ein neues Thema: stark getippt enum. Ich habe es ein bisschen recherchiert, aber bis jetzt kann ich nicht herausfinden, warum wir das brauchen und wozu es gut ist.

Zum Beispiel, wenn wir haben:

enum xyz{a, b, c};
/*a = 0, b = 1, c = 2, (Typical C format)*/

Warum müssen wir schreiben:

enum class xyz{a, b, c};

Was versuchen wir hier zu tun? Mein wichtigster Zweifel ist, wie man es benutzt. Könnten Sie ein kleines Beispiel nennen, das mich verständlich macht?

Rasmi Ranjan Nayak
quelle

Antworten:

114

OK, erstes Beispiel: Aufzählungen im alten Stil haben keinen eigenen Bereich:

enum Animals {Bear, Cat, Chicken};
enum Birds {Eagle, Duck, Chicken}; // error! Chicken has already been declared!

enum class Fruits { Apple, Pear, Orange };
enum class Colours { Blue, White, Orange }; // no problem!

Zweitens konvertieren sie implizit in integrale Typen, was zu seltsamem Verhalten führen kann:

bool b = Bear && Duck; // what?

Schließlich können Sie den zugrunde liegenden integralen Typ von C ++ 11-Aufzählungen angeben:

enum class Foo : char { A, B, C};

Bisher wurde der zugrunde liegende Typ nicht angegeben, was zu Kompatibilitätsproblemen zwischen Plattformen führen kann. Bearbeiten In Kommentaren wurde darauf hingewiesen, dass Sie in C ++ 11 auch den zugrunde liegenden integralen Typ einer Aufzählung im "alten Stil" angeben können.

Juanchopanza
quelle
Müssen wir deklarieren / definieren enum class Coloursund enum class Fruits. Denn als ich den Code in VS 2010 geschrieben habe, wirft er einen Fehler "expects a defination or a tag name"unter class.
Rasmi Ranjan Nayak
Außerdem: Für "normale" Aufzählung in C ++ 11 wie in C ++ 98 ist der zugrunde liegende Standardtyp nicht definiert
Bruziuz
2
Außerdem: Sie können eine Vorwärtsdeklaration von enum-s durchführen, wenn der zugrunde liegende Typ angegeben ist. Für gewöhnliche Aufzählungen in C ++ 11, C ++ 98 ist dies nicht zulässig. Der Microsoft-Compiler erlaubt es Ihnen, die Vorwärtsdeklaration von enum durchzuführen, aber es ist nur eine MS-Erweiterung, es ist kein Standard (zum Beispiel erlaubt gcc es nicht). So etwas ist jetzt legal: enum ForwardDeclare: std :: uint8_t;
Bruziuz
Können wir Enums mit Gültigkeitsbereich haben, die sich auch implizit in einen integralen Typ umwandeln?
SS Anne
17

Auf dieser IBM Seite gibt es einen guten Artikel über Aufzählungen , der sehr detailliert und gut geschrieben ist. Hier sind einige wichtige Punkte auf den Punkt gebracht:

Die Aufzählungen mit Gültigkeitsbereich lösen die meisten Einschränkungen, die bei regulären Aufzählungen auftreten: vollständige Typensicherheit, genau definierter zugrunde liegender Typ, Umfangsprobleme und Vorwärtsdeklaration.

  • Sie erhalten Typensicherheit, indem Sie alle impliziten Konvertierungen von Enums mit Gültigkeitsbereich in andere Typen nicht zulassen.
  • Sie erhalten einen neuen Bereich, und die Aufzählung befindet sich nicht mehr im umschließenden Bereich, wodurch Sie sich vor Namenskonflikten schützen.
  • Mit Enumes mit Gültigkeitsbereich können Sie den zugrunde liegenden Typ der Aufzählung angeben. Für Enums mit Gültigkeitsbereich wird standardmäßig int verwendet, wenn Sie ihn nicht angeben.
  • Jede Aufzählung mit einem festen zugrunde liegenden Typ kann vorwärts deklariert werden.
SingerOfTheFall
quelle
2
Der dritte und vierte Punkt sind nicht spezifisch für Aufzählungen mit Gültigkeitsbereich. Sie können den zugrunde liegenden Typ jeder Aufzählung angeben.
Mike Seymour
1
Hat jemand einen Link zu einer weniger kaputten Version des PDF? Die darin enthaltenen Codebeispiele werden in keinem meiner PDF-Viewer gerendert, was der Fantasie viel überlässt.
Sara Sinback
11

Werte von enum classsind wirklich vom Typ enum class, nicht underlying_typewie bei C-Aufzählungen.

enum xyz { a, b, c};
enum class xyz_c { d, f, e };

void f(xyz x)
{
}

void f_c(xyz_c x)
{
}

// OK.
f(0);
// OK for C++03 and C++11.
f(a);
// OK with C++11.
f(xyz::a);
// ERROR.
f_c(0);
// OK.
f_c(xyz_c::d);
Für immer
quelle
5

Die Aufzählungsklassen ("neue Aufzählungen", "starke Aufzählungen") behandeln drei Probleme mit herkömmlichen C ++ - Aufzählungen:

  1. konventionell enumsimplizit konvertieren in int, was zu Fehlern führt, wenn jemand nicht möchte, dass eine Aufzählung als Ganzzahl fungiert.
  2. konventionelle enumsexportieren ihre Enumeratoren in den umgebenden Bereich, was zu Namenskonflikten führt.
  3. Der zugrunde liegende Typ von a enumkann nicht angegeben werden, was zu Verwirrung und Kompatibilitätsproblemen führt und eine Vorwärtsdeklaration unmöglich macht.

enum class ("starke Aufzählungen") sind stark typisiert und haben einen Gültigkeitsbereich:

enum Alert { green, yellow, orange, red }; // traditional enum

enum class Color { red, blue };   // scoped and strongly typed enum
                                  // no export of enumerator names into enclosing scope
                                  // no implicit conversion to int
enum class TrafficLight { red, yellow, green };

Alert a = 7;              // error (as ever in C++)
Color c = 7;              // error: no int->Color conversion

int a2 = red;             // ok: Alert->int conversion
int a3 = Alert::red;      // error in C++98; ok in C++11
int a4 = blue;            // error: blue not in scope
int a5 = Color::blue;     // error: not Color->int conversion

Color a6 = Color::blue;   // ok

Wie gezeigt, funktionieren herkömmliche Aufzählungen wie gewohnt, aber Sie können sich jetzt optional mit dem Namen der Aufzählung qualifizieren.

Die neuen Aufzählungen sind "Aufzählungsklassen", da sie Aspekte traditioneller Aufzählungen (Namenswerte) mit Aspekten Klassen (Mitglieder mit Gültigkeitsbereich und Fehlen von Konvertierungen) kombinieren.

Die Möglichkeit, den zugrunde liegenden Typ anzugeben, ermöglicht eine einfachere Interoperabilität und garantierte Aufzählungsgrößen:

enum class Color : char { red, blue };  // compact representation

enum class TrafficLight { red, yellow, green };  // by default, the underlying type is int

enum E { E1 = 1, E2 = 2, Ebig = 0xFFFFFFF0U };   // how big is an E?
                                                 // (whatever the old rules say;
                                                 // i.e. "implementation defined")

enum EE : unsigned long { EE1 = 1, EE2 = 2, EEbig = 0xFFFFFFF0U };   // now we can be specific

Es ermöglicht auch die Vorwärtsdeklaration von Aufzählungen:

enum class Color_code : char;     // (forward) declaration
void foobar(Color_code* p);       // use of forward declaration
// ...
enum class Color_code : char { red, yellow, green, blue }; // definition

Der zugrunde liegende Typ muss einer der vorzeichenbehafteten oder vorzeichenlosen Ganzzahltypen sein. Der Standardwert ist int.

In der Standardbibliothek werden enumKlassen verwendet für:

  1. Zuordnungssystemspezifische Fehlercodes: In <system_error>: enum class errc;
  2. Zeigersicherheitsindikatoren: In <memory>:enum class pointer_safety { relaxed, preferred, strict };
  3. E / A-Stream-Fehler: In <iosfwd>:enum class io_errc { stream = 1 };
  4. Fehlerbehandlung bei asynchroner Kommunikation: In <future>:enum class future_errc { broken_promise, future_already_retrieved, promise_already_satisfied };

Einige von diesen haben Operatoren, wie z. B. ==definiert.

Jnana
quelle
3

Enum Scope

Aufzählungen exportieren ihre Aufzähler in den umgebenden Bereich. Dies hat zwei Nachteile. Erstens kann es zu Namenskonflikten kommen, wenn zwei Enumeratoren in unterschiedlichen Aufzählungen, die im selben Bereich deklariert sind, denselben Namen haben. Zweitens ist es nicht möglich, einen Enumerator mit einem vollständig qualifizierten Namen einschließlich des Aufzählungsnamens zu verwenden.

enum ESet {a0, a, a1, b1, c3};
enum EAlpha{a, b, c}

select = ESet::a; // error
select = a;       // is ambigious
RAM
quelle