Jak używać wyliczeń w C ++

218

Załóżmy, że mamy enumpodobne:

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

Chcę utworzyć instancję tego enumi zainicjować ją z odpowiednią wartością, więc:

Days day = Days.Saturday;

Teraz chcę sprawdzić moją zmienną lub instancję z istniejącą enumwartością, więc:

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

Co daje mi błąd kompilacji:

błąd: oczekiwane wyrażenie podstawowe przed „.” znak

Żeby było jasne, jaka jest różnica między powiedzeniem:

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

i

if (day == Saturday)

?

Co tak naprawdę odnoszą się do tych dwóch, że jest OK, a drugi powoduje błąd kompilacji?

Rika
źródło
4
wiem, chcę wiedzieć, dlaczego daje mi błąd!
Rika,
1
Tu jest środa. Masz zbyt dużo błędów składniowych dla kompilatora C ++. Począwszy od „Enum”.
Öö Tiib,
1
@Hossein, ponieważ wyliczenia nie są taką samą składnią (i semantyką) w obu językach. Pierwszą rzeczą, którą robię po wystąpieniu błędu podczas próby użycia funkcji w nowym języku, jest sprawdzenie składni (lub jeśli to możliwe) w tym języku.
Chris
@chris: Wiem, robię dokładnie to samo. mam nadzieję, że dostałem odpowiedź. Ja również zaktualizowałem pytanie, aby było bardziej zrozumiałe. Przy okazji, dziękuję;)
Rika
17
o ile wiem, deklaracja wyliczenia i użycie w tych dwóch językach są takie same. ”. Tam jest twój problem. C # nie jest tym samym językiem, co C ++. W szczególności mają różną składnię dla wyliczeń.
Robᵩ

Odpowiedzi:

350

Ten kod jest nieprawidłowy:

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

Ponieważ Daysnie jest zakresem ani przedmiotem. To jest typ. A same typy nie mają członków. To, co napisałeś, jest odpowiednikiem std::string.clear. std::stringjest typem, więc nie możesz go używać .. Używasz .na instancji klasy.

Niestety wyliczenia są magiczne, więc analogia się tam kończy. Ponieważ w przypadku klasy można std::string::clearuzyskać wskaźnik do funkcji składowej, ale w C ++ 03 Days::Sundayjest niepoprawny. (Co jest smutne). Wynika to z tego, że C ++ jest (nieco) wstecznie kompatybilny z C, a C nie ma przestrzeni nazw, więc wyliczenia musiały znajdować się w globalnej przestrzeni nazw. Więc składnia jest po prostu:

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

Na szczęście Mike Seymour zauważa, że ​​zostało to rozwiązane w C ++ 11. Zmień enumsię enum classi uzyska swój zakres; Days::Sundayjest więc nie tylko poprawny, ale jest jedynym sposobem na dostęp Sunday. Szczęśliwe dni!

Mucząca Kaczka
źródło
254
Na szczęście twoja skarga została rozwiązana w C ++ 11. Zmień enumsię enum classi uzyska swój zakres; Days::Sundayjest więc nie tylko poprawny, ale jest jedynym sposobem na dostęp Sunday. Szczęśliwe dni!
Mike Seymour
11
Uwielbiam komunikaty o błędach C ++ ... udowadniają, że język jest nieporęczny, aby dać dobre opinie. Przyjmuję, że „pierwotne wyrażenie” to obiekt lub zakres lub coś innego, co NIE jest typem. Być może typ jest „wyrażeniem wtórnym”. A jak to programista C ++ mógłby nazwać „operatorem kropkowym”, kompilator C ++ może wywoływać tylko „token”. Kiedy trudno jest zrozumieć komunikaty o błędach, myślę, że coś jest nie tak z językiem.
Travis,
4
@Travis: en.cppreference.com/w/cpp/language/… . Wyrażenie podstawowe jest tylko pierwszą rzeczą w wyrażeniu, zwykle jest to nazwa, zmienna lub literał. Co do drugiej części, nie widzę dużej różnicy między '.' tokeni dot operator, poza tym, że jest to token, a nie operator, i pokazuje dokładny symbol, a nie nazwę.
Mooing Duck
@Mike Seymour Próbowałem uzyskać dostęp do wyliczeń bez operatorów rozdzielczości zakresu w wielu kompilatorach i wydaje się, że działa. Powiedziałeś, że od C ++ 11 jest to jedyny sposób, z jakiegoś powodu mogę po prostu uzyskać dostęp do wartości wyliczeniowych jako globale, nie potrzebuję ::
Zebrafish
1
@TitoneMaurice: Jeśli masz enum, nie możesz użyć zakresu ani globalnego zakresu ( ::Saturday). Jeśli masz enum class(co jest bardzo inna sprawa), to mają do użytku Days::Saturday.
Kaczka Mooing
24

To wystarczy, aby zadeklarować zmienną enum i porównać ją:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Saturday;
if (day == Saturday) {
    std::cout << "Ok its Saturday";
}
matematyk 1975
źródło
dlaczego źle jest powiedzieć, czy (dzień == dni.Satudday)? muszą być takie same, więc dlaczego kompilator narzeka na to?
Rika,
1
@Hossein wartości zadeklarowane w twoim wyliczeniu nie zachowują się jak zmienne klasy lub struktury członka. Nie jest to poprawna składnia do użycia
mathematician1975,
2
@Hossein: ponieważ Daysnie jest zakresem ani przedmiotem. To jest typ. A same typy nie mają członków. std::string.clearrównież nie kompiluje się z tego samego powodu.
Mooing Duck
8
@Hossein: Ponieważ nie tak działają wyliczenia w C ++. Nieokreślone wyliczenia umieszczają swoje wartości w otaczającej przestrzeni nazw; te (lunetą enum class, nowe w 2011 roku) posiada swój własny zakres i są dostępne za pomocą operatora zakresu, Days::Saturday. Operator dostępu do elementu ( .) służy wyłącznie do uzyskiwania dostępu do elementów klasy.
Mike Seymour,
@MooingDUck i MikeSeymour Czy któryś z was opublikuje odpowiedź jako odpowiedź? bo tego właśnie szukałem, wydając to pytanie;)
Rika
22

Znaczna część tego powinna dać błędy kompilacji.

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

Teraz Saturday, Sundayitp mogą być stosowane jako najwyższego poziomu gołymi stałych i Daysmoże być używany jako typ:

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

I podobnie później, aby przetestować:

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

Te enumwartości są jak gołymi stałych - Są un -scoped - przy odrobinie pomocy ze strony kompilatora: (chyba, że używasz C ++ 11 klas enum ) oni nie są zamknięte jak przedmiot lub członków struktury, na przykład, i Państwo nie może odnosić się do nich jako członków o Days.

Będziesz mieć to, czego szukasz z C ++ 11 , który wprowadza enum class:

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

// ...

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

Zauważ, że ten C ++ różni się nieco od C na kilka sposobów, jeden jest taki, że C wymaga użycia enumsłowa kluczowego podczas deklarowania zmiennej:

// day declaration in C:
enum Days day = Saturday;
pb2q
źródło
Zaktualizowałem pytanie, myślę, że teraz jest jaśniejsze, po co dokładnie jestem :) Przy okazji dziękuję :)
Rika
14

Możesz użyć sztuczki, aby używać zakresów według własnego uznania, po prostu zadeklaruj enum w następujący sposób:

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

Days::type day = Days::Saturday;
if (day == Days::Saturday)
ataman1x
źródło
9

Zamiast używać szeregu instrukcji if, wyliczenia nadają się dobrze do przełączania instrukcji

Używam niektórych kombinacji wyliczania / przełączania w konstruktorze poziomów, który buduję dla mojej gry.

EDYCJA: Kolejna rzecz, widzę, że chcesz składnię podobną do;

if(day == Days.Saturday)
etc

Możesz to zrobić w C ++:

if(day == Days::Saturday)
etc

Oto bardzo prosty przykład:

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;
}
Dean Knight
źródło
Miłą rzeczą jest to, że kompilatory powiedzą ci, jeśli przegapiłeś włożenie sprawy.
Chris
Czy w tym przypadku nie powinieneś używać klasy enum?
Rika
1
enum jest po prostu typem danych w C ++ Tak więc zadeklarowanie wyliczenia, jak to zrobiłem powyżej w pliku .h, a następnie włączenie tego pliku do dowolnego pliku .cpp, w którym chcesz go użyć, da ci dostęp do wyliczenia. Właśnie zauważyłem, że zapomniałem dodać #include w moim przykładzie .cpp. Redagowanie.
Dean Knight
Widzę też, że ktoś inny mówi, że wyliczenia w C ++ są globalne. Z mojego doświadczenia wynika, że ​​używając wyliczeń w sposób, w jaki mam wyżej, mogę uzyskać do nich dostęp tylko po dodaniu .h. Wydaje się więc, że to także powstrzymuje globalny dostęp, co zawsze jest dobre. EDYCJA: Wygląda na to, że nieświadomie używam wyliczeń w sposób C ++ 11, jeśli dobrze czytam ...
Dean Knight
9

Jeśli nadal używasz C ++ 03 i chcesz używać wyliczeń, powinieneś używać wyliczeń wewnątrz przestrzeni nazw. Na przykład:

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

Możesz użyć wyliczenia poza przestrzenią nazw, np.

Daysofweek::Days day = Daysofweek::Saturday;

if (day == Daysofweek::Saturday)
{
    std::cout<<"Ok its Saturday";
}
San
źródło
8

Szukasz silnie typowanych wyliczeń , funkcja dostępna w standardzie C ++ 11 . Zamienia wyliczenia w klasy o wartościach zasięgu.

Korzystając z własnego kodu przykład:

  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

Użycie ::jako akcesorium do wyliczeń zakończy się niepowodzeniem, jeśli będzie się celować w standard C ++ wcześniejszy niż C ++ 11. Ale niektóre stare kompilatory go nie obsługują, a niektóre IDE po prostu zastępują tę opcję i ustawiają stary standard C ++.

Jeśli używasz GCC, włącz C + 11 za pomocą -std = c ++ 11 lub -std = gnu11 .

Bądź szczęśliwy!

Alex Byrth
źródło
1
Zapomniałeś napisać enum class Days { ....
Martin Hennings
W rzeczy samej. naprawić to! Dzięki.
Alex Byrth,
7

To nie powinno działać w C ++:

Days.Saturday

Dni nie jest zakresem ani obiektem zawierającym członków, do których można uzyskać dostęp za pomocą operatora kropki. Ta składnia jest tylko językiem C # i nie jest legalna w C ++.

Microsoft od dawna utrzymuje rozszerzenie C ++, które umożliwia dostęp do identyfikatorów za pomocą operatora zasięgu:

enum E { A, B, C };

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

Ale jest to niestandardowe przed C ++ 11. W C ++ 03 identyfikatory zadeklarowane w wyliczeniu istnieją tylko w tym samym zakresie, co sam typ wyliczenia.

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

C ++ 11 zezwala na kwalifikowanie identyfikatorów enum za pomocą nazwy enum, a także wprowadza klasy enum, które tworzą nowy zakres dla identyfikatorów zamiast umieszczać je w otaczającym zakresie.

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

enum class F { A, B, C };

A; // error
F::B;
bames53
źródło
4

Niestety elementy wyliczenia są „globalne”. Masz do nich dostęp, robiąc day = Saturday. Oznacza to, że nie możesz mieć enum A { a, b } ;i enum B { b, a } ;są w konflikcie.

Grzegorz
źródło
2
enum classTo znaczy, dopóki nie użyjesz w C ++ 11. Wcześniej musisz robić zajęcia pozorowane.
Chris
Nie znam C ++ 11. Zakładam, że pytanie dotyczy C ++. Tak, użycie klas lub przestrzeni nazw załatwi sprawę.
Grzegorz
@Grzegorz: Myślę, że Chris odnosi się do nowo wprowadzonej klasy enum, która zapewnia silnie typowane wyliczenia.
Rika
@Hossein: Dziękujemy za zwrócenie na to uwagi. Znalazłem wyjaśnienie klasy num i wiem, o czym mówił Chris. Wielkie dzięki.
Grzegorz
@Grzegorz: Nie chciałem lekceważyć, po prostu pomyślałem, że mogę pomóc, przepraszam za wszelkie prawdopodobne nieporozumienia. Jeszcze raz dziękuję za poświęcony czas i pomoc;)
Rika
4

Podczas gdy C ++ (z wyjątkiem C ++ 11) ma wyliczenia, ich wartości są „wyciekane” do globalnej przestrzeni nazw.
Jeśli nie chcesz, aby wyciekły (i NIE POTRZEBUJESZ użyć typu wyliczeniowego), rozważ następujące kwestie:

class EnumName {  
   public:   
      static int EnumVal1;  
      (more definitions)  
};  
EnumName::EnumVal1 = {value};  
if ([your value] == EnumName::EnumVal1)  ...
InitializeSahib
źródło
3

Wyliczenia w C ++ są jak liczby całkowite zamaskowane nazwami, które im podajesz, kiedy deklarujesz swoje wartości wyliczeniowe (nie jest to definicja, tylko wskazówka, jak to działa).

Ale w twoim kodzie są dwa błędy:

  1. Przeliteruj enumwszystkie małe litery
  2. Nie potrzebujesz Days.przed sobotą.
  3. Jeśli to wyliczenie jest zadeklarowane w klasie, użyj if (day == YourClass::Saturday){}
Balázs Édes
źródło
OP zmienił pisownię / przypadek 16 minut po początkowym poście ( wersja 1 na wersja 2 ).
Peter Mortensen
1

Myślę, że twoim głównym problemem jest użycie .zamiast ::, który użyje przestrzeni nazw.

Próbować:

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";
}
James Oravec
źródło
To nie działa: aby użyć Days::zakresu jak w twoim przykładzie, musisz zdefiniować wyliczenie za enum class Dayspomocą rozszerzenia C ++ 03 + Microsoft lub C ++ 11.
Futal
@Futal, powyższe działało z Borland C ++ Builder. Smak / Wersja C ++ nie jest kwestionowana.
James Oravec,
1
Twoja wersja Borland C ++ Builder musi używać C ++ 11 lub nowszej wersji. Zarówno Gcc, jak i Clang podają błędy lub ostrzeżenia, jeśli twój przykład jest skompilowany z -std=c++98lub -std=c++03. Clang jest dość oczywiste: warning: use of enumeration in a nested name specifier is a C++11 extension.
Futal
1

Jeśli chcemy ścisłego bezpieczeństwa typu i wyliczania zakresu, używanie enum classjest dobre w C ++ 11.

Gdybyśmy musieli pracować w C ++ 98, moglibyśmy skorzystać z porady udzielonej przez InitializeSahib, Sanaby włączyć wyliczanie zakresu.

Jeśli chcemy również ścisłego bezpieczeństwa typu, poniższy kod może zaimplementować coś takiego 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;
}

Kod został zmodyfikowany na podstawie przykładu klasy miesiąc w książce Effective C ++ 3rd: Item 18

Xu Hui
źródło
-15

Przede wszystkim wpisz „E” in enum, „e” jako małą literę.

Po drugie, w polu „Days.Saturday” wpisz nazwę „Days”.

Po trzecie ... kup sobie dobrą książkę C ++.

vikramjitSingh
źródło
5
Przepraszam, że otrzymałeś wszystkie głosy negatywne (to znaczy, odpowiedź zasługuje na to), ale to nie znaczy, że musisz opuścić społeczność na 6 lat. Wróć i dołącz do nas. Ty też masz coś do wniesienia. Być pomocnym. Dzielić się wiedzą.
Gabriel Staples