Verwendung von Aufzählungen in C ++


218

Angenommen, wir haben enumFolgendes:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};

Ich möchte eine Instanz davon erstellen enumund sie mit einem geeigneten Wert initialisieren, also mache ich:

Days day = Days.Saturday;

Jetzt möchte ich meine Variable oder Instanz mit einem vorhandenen enumWert überprüfen , also mache ich:

if (day == Days.Saturday)
{
    std::cout << "Ok its Saturday";
}

Was mir einen Kompilierungsfehler gibt:

Fehler: erwarteter primärer Ausdruck vor '.' Zeichen

Um klar zu sein, was ist der Unterschied zwischen den Worten:

if (day == Days.Saturday) // Causes compilation error

und

if (day == Saturday)

?

Worauf beziehen sich diese beiden eigentlich, da einer in Ordnung ist und einer einen Kompilierungsfehler verursacht?


4
Ich weiß, ich möchte wissen, warum es mir den Fehler gibt!
Rika

1
Es ist Mittwoch hier. Sie haben zu viele Syntaxfehler für den C ++ - Compiler. Ausgehend von 'Enum'.
Öö Tiib

1
@Hossein, weil Aufzählungen in beiden Sprachen nicht die gleiche Syntax (und Semantik) haben. Das erste, was ich mache, nachdem ich beim Versuch, eine Funktion in einer neuen Sprache zu verwenden, einen Fehler erhalten habe, ist, die Syntax (oder wenn möglich) in dieser Sprache nachzuschlagen.
chris

@chris: Ich weiß, ich mache genau das gleiche. Hoffentlich habe ich meine Antwort bekommen. Ich habe auch die Frage aktualisiert, um klarer zu sein. Danke übrigens;)
Rika

17
" Soweit ich weiß, sind die Aufzählungserklärung und die Verwendung in diesen beiden Sprachen gleich. " Da ist dein Problem, genau dort. C # ist nicht dieselbe Sprache wie C ++. Insbesondere haben sie unterschiedliche Syntax für Aufzählungen.
Robᵩ

Antworten:


350

Dieser Code ist falsch:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Days.Saturday;
if (day == Days.Saturday)

Weil Dayses weder ein Bereich noch ein Objekt ist. Es ist ein Typ. Und Typen selbst haben keine Mitglieder. Was Sie geschrieben haben, entspricht std::string.clear. std::stringist ein Typ, den Sie nicht verwenden .können. Sie verwenden .für eine Instanz einer Klasse.

Leider sind Aufzählungen magisch und so hört die Analogie hier auf. Denn mit einer Klasse können Sie std::string::cleareinen Zeiger auf die Member-Funktion abrufen, der in C ++ 03 Days::Sundayjedoch ungültig ist. (Was traurig ist). Dies liegt daran, dass C ++ (etwas) abwärtskompatibel mit C ist und C keine Namespaces hatte, sodass Aufzählungen im globalen Namespace sein mussten. Die Syntax lautet also einfach:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Saturday;
if (day == Saturday)

Glücklicherweise stellt Mike Seymour fest , dass dies in C ++ 11 behoben wurde. Wechseln Sie enumzu enum classund es bekommt seinen eigenen Umfang; Dies Days::Sundayist nicht nur gültig, sondern auch die einzige Möglichkeit, darauf zuzugreifen Sunday. Glückliche Tage!


254
Glücklicherweise wurde Ihre Beschwerde in C ++ 11 behandelt. Wechseln Sie enumzu enum classund es bekommt seinen eigenen Umfang; Dies Days::Sundayist nicht nur gültig, sondern auch die einzige Möglichkeit, darauf zuzugreifen Sunday. Glückliche Tage!
Mike Seymour

10
Ich muss die C ++ - Fehlermeldungen lieben ... sie beweisen, dass die Sprache zu umständlich ist, um überhaupt ein gutes Feedback zu geben. Ich nehme an, ein 'primärer Ausdruck' ist ein Objekt oder ein Bereich oder eine andere Sache, die KEIN Typ ist. Vielleicht ist ein Typ ein 'sekundärer Ausdruck'. Und was ein C ++ - Entwickler als "Punktoperator" bezeichnen könnte, kann der C ++ - Compiler nur als "Token" bezeichnen. Wenn es so schwer wird, die Fehlermeldungen zu verstehen, stimmt etwas nicht mit der Sprache, die ich denke.
Travis

4
@Travis: en.cppreference.com/w/cpp/language/… . Ein primärer Ausdruck ist nur das erste in einem Ausdruck, normalerweise ein Name oder eine Variable oder ein Literal. Was den zweiten Teil betrifft, sehe ich keinen großen Unterschied zwischen '.' tokenund dot operator, außer es ist ein Token und kein Operator, und es zeigt das genaue Symbol und nicht einen Namen.
Mooing Duck

@ Mike Seymour Ich habe versucht, auf die Enums ohne die Operatoren für die Bereichsauflösung auf einer Reihe von Compilern zuzugreifen, und es scheint zu funktionieren. Sie sagten, ab C ++ 11 ist dies der einzige Weg, aus irgendeinem Grund kann ich einfach als Globale auf die Enum-Werte zugreifen und brauche den ::
Zebrafisch

1
@TitoneMaurice: Wenn Sie einen haben enum, können Sie keinen Bereich oder den globalen Bereich ( ::Saturday) verwenden. Wenn Sie eine haben enum class(das ist eine ganz andere Sache ist), dann Sie haben zu verwenden Days::Saturday.
Mooing Duck

24

Dies reicht aus, um Ihre Enum-Variable zu deklarieren und zu vergleichen:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Saturday;
if (day == Saturday) {
    std::cout << "Ok its Saturday";
}

Warum ist es falsch zu sagen, ob (Tag == Tage.Satudtag)? Sie müssen gleich sein. Warum beschwert sich der Compiler darüber?
Rika

1
@Hossein Die in Ihrer Aufzählung deklarierten Werte verhalten sich nicht wie Klassen- oder Strukturelementvariablen. Dies ist nicht die richtige Syntax zu verwenden
Mathematiker1975

2
@Hossein: weil Dayses weder ein Bereich noch ein Objekt ist. Es ist ein Typ. Und Typen selbst haben keine Mitglieder. std::string.clearkann aus dem gleichen Grund auch nicht kompiliert werden.
Mooing Duck

7
@Hossein: Weil Enums in C ++ nicht so funktionieren. Aufzählungen ohne Gültigkeitsbereich setzen ihre Werte in den umgebenden Namespace. Bereiche mit Gültigkeitsbereich ( enum classneu in 2011) haben einen eigenen Bereich und werden über den Bereichsoperator aufgerufen Days::Saturday. Der Member Access Operator ( .) wird nur für den Zugriff auf Klassenmitglieder verwendet.
Mike Seymour

@MooingDUck und MikeSeymour Würde einer von euch eure Antwort als Antwort posten? denn genau das war ich, als ich diese Frage stellte;)
Rika

22

Vieles davon sollte zu Kompilierungsfehlern führen.

// note the lower case enum keyword
enum Days { Saturday, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday };

Nun Saturday, Sundayusw. können als Top-Level - bare Konstanten verwendet werden, und Dayskann als eine Art verwendet werden:

Days day = Saturday;   // Days.Saturday is an error

Und ähnlich später, um zu testen:

if (day == Saturday)
    // ...

Diese enumWerte sind wie nackte Konstanten - sie sind un -scoped - mit ein wenig mehr Hilfe vom Compiler: (es sei denn , Sie verwenden C ++ 11 Enum - Klassen ) sie sind nicht wie Objekt oder Strukturelemente beispielsweise eingekapselt, und Sie können sie nicht als Mitglieder von bezeichnen Days.

Sie müssen , was Sie für mit der Suche C ++ 11 , die eine Einführung enum class:

enum class Days
{
    SUNDAY,
    MONDAY,
    // ... etc.
}

// ...

if (day == Days::SUNDAY)
    // ...

Beachten Sie, dass sich C ++ in einigen Punkten ein wenig von C unterscheidet. Zum einen erfordert C die Verwendung des enumSchlüsselworts, wenn eine Variable deklariert wird:

// day declaration in C:
enum Days day = Saturday;

Ich habe die Frage aktualisiert, ich denke, es ist jetzt klarer, was ich genau danach bin :) Übrigens danke :)
Rika

13

Sie können einen Trick verwenden, um Bereiche nach Ihren Wünschen zu verwenden. Deklarieren Sie einfach die Aufzählung folgendermaßen:

struct Days 
{
   enum type
   {
      Saturday,Sunday,Tuesday,Wednesday,Thursday,Friday
   };
};

Days::type day = Days::Saturday;
if (day == Days::Saturday)

9

Anstatt eine Reihe von if-Anweisungen zu verwenden, eignen sich Aufzählungen gut, um Anweisungen zu wechseln

Ich verwende einige Enum / Switch-Kombinationen in dem Level Builder, den ich für mein Spiel baue.

EDIT: Eine andere Sache, ich sehe, Sie wollen eine ähnliche Syntax;

if(day == Days.Saturday)
etc

Sie können dies in C ++ tun:

if(day == Days::Saturday)
etc

Hier ist ein sehr einfaches Beispiel:

EnumAppState.h

#ifndef ENUMAPPSTATE_H
#define ENUMAPPSTATE_H
enum eAppState
{
    STARTUP,
    EDIT,
    ZONECREATION,
    SHUTDOWN,
    NOCHANGE
};
#endif

Somefile.cpp

#include "EnumAppState.h"
eAppState state = eAppState::STARTUP;
switch(state)
{
case STARTUP:
    //Do stuff
    break;
case EDIT:
    //Do stuff
    break;
case ZONECREATION:
    //Do stuff
    break;
case SHUTDOWN:
    //Do stuff
    break;
case NOCHANGE:
    //Do stuff
    break;
}

Das Schöne hier ist, dass Compiler Ihnen sagen, ob Sie einen Fall verpasst haben.
Chris

Sollten Sie in diesem Fall nicht die Klassenaufzählung verwenden?
Rika

1
enum ist nur ein Datentyp in C ++ Wenn Sie also eine Aufzählung wie oben in einer .h-Datei deklarieren und diese Datei dann in die CPP-Datei aufnehmen, in der Sie sie verwenden möchten, erhalten Sie Zugriff auf die Aufzählung. Ich habe gerade bemerkt, dass ich vergessen habe, das #include in mein .cpp-Beispiel einzufügen. Bearbeitung.
Dean Knight

Außerdem sehe ich jemanden, der sagt, dass Aufzählungen in C ++ global sind. Nach meiner Erfahrung kann ich mit Aufzählungen wie oben nur auf sie zugreifen, wenn ich die .h-Datei eingefügt habe. Dies scheint also auch den globalen Zugang zu stoppen, was immer gut ist. EDIT: Es scheint, als würde ich unwissentlich Enums in C ++ 11 verwenden, wenn ich die Dinge richtig lese ...
Dean Knight

8

Wenn Sie immer noch C ++ 03 verwenden und Aufzählungen verwenden möchten, sollten Sie Aufzählungen in einem Namespace verwenden. Z.B:

namespace Daysofweek{
enum Days {Saturday, Sunday, Tuesday,Wednesday, Thursday, Friday};
}

Sie können die Aufzählung außerhalb des Namespace wie folgt verwenden:

Daysofweek::Days day = Daysofweek::Saturday;

if (day == Daysofweek::Saturday)
{
    std::cout<<"Ok its Saturday";
}

7

Sie suchen nach stark typisierten Aufzählungen , eine Funktion, die im C ++ 11- Standard verfügbar ist . Aufzählungen werden in Klassen mit Bereichswerten umgewandelt.

Anhand Ihres eigenen Codebeispiels ist dies:

  enum class Days {Saturday, Sunday, Tuesday,Wednesday, Thursday, Friday};
  Days day = Days::Saturday;

  if (day == Days::Saturday)  {
    cout << " Today is Saturday !" << endl;
  }
  //int day2 = Days::Sunday; // Error! invalid

Die Verwendung ::als Accessoren für Aufzählungen schlägt fehl, wenn auf einen C ++ - Standard vor C ++ 11 abgezielt wird. Einige alte Compiler unterstützen dies jedoch nicht. Einige IDEs überschreiben diese Option einfach und legen einen alten C ++ - Standard fest.

Wenn Sie GCC verwenden, aktivieren Sie C + 11 mit -std = c ++ 11 oder -std = gnu11 .

Sei glücklich!


1
Du hast vergessen zu schreiben enum class Days { ....
Martin Hennings

Tatsächlich. es reparieren! Vielen Dank.
Alex Byrth

6

Dies sollte in C ++ nicht funktionieren:

Days.Saturday

Tage ist kein Bereich oder Objekt, das Mitglieder enthält, auf die Sie mit dem Punktoperator zugreifen können. Diese Syntax ist nur ein C # -Imismus und in C ++ nicht zulässig.

Microsoft hat seit langem eine C ++ - Erweiterung beibehalten, mit der Sie mithilfe des Bereichsoperators auf die Bezeichner zugreifen können:

enum E { A, B, C };

A;
E::B; // works with Microsoft's extension

Dies ist jedoch vor C ++ 11 kein Standard. In C ++ 03 existieren die in einer Aufzählung deklarierten Bezeichner nur im selben Bereich wie der Aufzählungstyp selbst.

A;
E::B; // error in C++03

In C ++ 11 ist es zulässig, Enum-Bezeichner mit dem Enum-Namen zu qualifizieren, und es werden Enum-Klassen eingeführt, die einen neuen Bereich für die Bezeichner erstellen, anstatt sie in den umgebenden Bereich zu setzen.

A;
E::B; // legal in C++11

enum class F { A, B, C };

A; // error
F::B;

4

Während C ++ (außer C ++ 11) Aufzählungen enthält, werden die darin enthaltenen Werte in den globalen Namespace "durchgesickert".
Wenn Sie nicht möchten, dass sie durchgesickert sind (und nicht den Aufzählungstyp verwenden MÜSSEN), beachten Sie Folgendes:

class EnumName {  
   public:   
      static int EnumVal1;  
      (more definitions)  
};  
EnumName::EnumVal1 = {value};  
if ([your value] == EnumName::EnumVal1)  ...

3

Leider sind Elemente der Aufzählung "global". Sie greifen auf sie zu, indem Sie dies tun day = Saturday. Das bedeutet, dass Sie nicht haben können enum A { a, b } ;und enum B { b, a } ;für sie in Konflikt stehen.


2
Bis Sie enum classin C ++ 11 verwenden. Vorher müssen Sie Dummy-Klassen erstellen.
Chris

Ich kenne C ++ 11 nicht. Ich gehe davon aus, dass sich die Frage auf C ++ bezieht. Ja, die Verwendung von Klassen oder Namespaces reicht aus.
Grzegorz

@Grzegorz: Ich denke, Chris bezieht sich auf die neu eingeführte Enum-Klasse, die stark typisierte Enums bietet.
Rika

@Hossein: Danke, dass du darauf hingewiesen hast. Ich habe eine Erklärung für die Num-Klasse gefunden und weiß, wovon Chris sprach. Vielen Dank.
Grzegorz

@Grzegorz: Ich wollte nicht respektlos sein, dachte nur, ich könnte helfen, entschuldige mich für ein mögliches Missverständnis. Ich danke Ihnen nochmals für Ihre Zeit und helfe mir;)
Rika

3

Aufzählungen in C ++ sind wie Ganzzahlen, die durch die Namen maskiert werden, die Sie ihnen geben, wenn Sie Ihre Aufzählungswerte deklarieren (dies ist keine Definition, sondern nur ein Hinweis darauf, wie es funktioniert).

Ihr Code enthält jedoch zwei Fehler:

  1. Buchstabiere enumalle Kleinbuchstaben
  2. Du brauchst das nicht Days.vor Samstag.
  3. Wenn diese Aufzählung in einer Klasse deklariert ist, verwenden Sie if (day == YourClass::Saturday){}

Das OP änderte die Schreibweise / den Fall 16 Minuten nach dem ersten Beitrag ( Revision 1 zu Revision 2 ).
Peter Mortensen

1

Ich denke, Ihr Hauptproblem ist die Verwendung von .anstelle von ::, wodurch der Namespace verwendet wird.

Versuchen:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Days::Saturday;
if(Days::Saturday == day)  // I like literals before variables :)
{
    std::cout<<"Ok its Saturday";
}

Dies funktioniert nicht: Um den Days::Bereich wie in Ihrem Beispiel zu verwenden, müssen Sie die Aufzählung mit enum class DaysC ++ 03 + Microsoft-Erweiterung oder C ++ 11 definieren und verwenden.
Futal

@Futal, das oben genannte lief mit Borland C ++ Builder. Geschmack / Version von C ++ kommt nicht in Frage.
James Oravec

1
Ihre Version von Borland C ++ Builder muss C ++ 11 oder neuer verwenden. Gcc und Clang geben beide Fehler oder Warnungen aus, wenn Ihr Beispiel mit -std=c++98oder kompiliert wurde -std=c++03. Clang ist ganz klar : warning: use of enumeration in a nested name specifier is a C++11 extension.
Futal

1

Wenn wir die strenge Typensicherheit und die Enumeration mit Gültigkeitsbereich wünschen, ist die Verwendung enum classin C ++ 11 gut.

Wenn wir in C ++ 98 arbeiten mussten, können wir die Ratschläge von verwenden InitializeSahib, Sanum die Enumeration mit Gültigkeitsbereich zu aktivieren.

Wenn wir auch die strenge Typensicherheit wollen, kann der folgende Code so etwas implementieren enum.

#include <iostream>
class Color
{
public:
    static Color RED()
    {
        return Color(0);
    }
    static Color BLUE()
    {
        return Color(1);
    }
    bool operator==(const Color &rhs) const
    {
        return this->value == rhs.value;
    }
    bool operator!=(const Color &rhs) const
    {
        return !(*this == rhs);
    }

private:
    explicit Color(int value_) : value(value_) {}
    int value;
};

int main()
{
    Color color = Color::RED();
    if (color == Color::RED())
    {
        std::cout << "red" << std::endl;
    }
    return 0;
}

Der Code wurde gegenüber dem Beispiel für die Klasse Monat in Buch Effective C ++ 3: Punkt 18 geändert


-15

Machen Sie zuerst 'E' in Aufzählung, 'e' als Kleinbuchstaben.

Zweitens geben Sie den Typnamen 'Days' in 'Days.Saturday' ein.

Drittens ... kaufen Sie sich ein gutes C ++ - Buch.


5
Tut mir leid, dass Sie all diese Abstimmungen erhalten haben (ich meine, die Antwort hat es irgendwie verdient), aber das bedeutet nicht, dass Sie die Community für 6 Jahre verlassen müssen. Komm zurück und mach mit. Sie haben auch etwas beizutragen. Sei hilfreich. Wissen teilen.
Gabriel Staples
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.