Как использовать перечисления в C ++

218

Предположим, у нас есть что-то enumвроде следующего:

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

Я хочу создать экземпляр этого enumи инициализировать его с надлежащим значением, поэтому я делаю:

Days day = Days.Saturday;

Теперь я хочу проверить мою переменную или экземпляр с существующим enumзначением, поэтому я делаю:

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

Что дает мне ошибку компиляции:

ошибка: ожидаемое первичное выражение перед '.' знак

Итак, чтобы было ясно, в чем разница между высказыванием:

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

и

if (day == Saturday)

?

Что эти два на самом деле ссылаются, в том, что один в порядке, а другой вызывает ошибку компиляции?

Rika
источник
4
я знаю, я хочу знать, почему это дает мне ошибку!
Рика
1
Здесь среда. У вас слишком много синтаксических ошибок для компилятора C ++. Начиная с «Enum».
Öö Tiib
1
@Hossein, потому что перечисления не имеют одинаковый синтаксис (и семантику) в обоих языках. Первое, что я делаю после получения ошибки при попытке использовать функцию на новом языке, - это поиск синтаксиса (или, если это возможно) на этом языке.
Крис
@ Крис: Я знаю, я делаю ту же самую вещь. К счастью, я получил свой ответ. Я также обновил вопрос, чтобы быть более ясным. Спасибо, кстати;)
Рика
17
« Насколько я знаю, объявление и использование перечислений на этих двух языках одинаковы ». Там твоя проблема, прямо там. C # не тот же язык, что и C ++. В частности, они имеют разный синтаксис для перечислений.
Робо

Ответы:

350

Этот код неверен:

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

Потому что Daysэто не сфера и не объект. Это тип. И сами типы не имеют членов. То, что вы написали, эквивалентно std::string.clear. std::stringэто тип, поэтому вы не можете использовать .его. Вы используете .на экземпляре класса.

К сожалению, перечисления являются волшебными, и аналогия на этом заканчивается. Потому что с классом вы можете сделать, std::string::clearчтобы получить указатель на функцию-член, но в C ++ 03, Days::Sundayявляется недействительным. (Что грустно). Это потому, что C ++ (в некоторой степени) обратно совместим с C, а C не имеет пространств имен, поэтому перечисления должны были находиться в глобальном пространстве имен. Таким образом, синтаксис просто:

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

К счастью, Майк Сеймур отмечает, что это было решено в C ++ 11. Измените enumна enum classи получите свою собственную область; это Days::Sundayне только допустимо, но и является единственным способом доступа Sunday. Счастливые дни!

Мытье утка
источник
254
К счастью, ваша жалоба была рассмотрена в C ++ 11. Измените enumна enum classи получите свою собственную область; это Days::Sundayне только допустимо, но и является единственным способом доступа Sunday. Счастливые дни!
Майк Сеймур
11
Должен любить сообщения об ошибках C ++ ... они доказывают, что язык слишком громоздкий, чтобы давать хорошие отзывы. Я понимаю, что «первичное выражение» - это объект, область видимости или другая вещь, которая НЕ является типом. Возможно, тип - это «вторичное выражение». И то, что разработчик C ++ может назвать «оператором точки», компилятор C ++ может назвать только «токеном». Когда становится трудно понять сообщения об ошибках, я думаю, что-то не так с языком.
Трэвис
4
@Travis: en.cppreference.com/w/cpp/language/… . Первичное выражение - это просто первая вещь в выражении, обычно это имя, переменная или литерал. Что касается второй части, я не вижу большой разницы между '.' tokenи dot operator, кроме того, что это токен, а не оператор, и он показывает точный символ, а не имя.
Утка
@Mike Seymour Я пытался получить доступ к перечислениям без операторов разрешения контекста на нескольких компиляторах, и это, похоже, работает. Вы сказали, что для C ++ 11 это единственный способ, по какой-то причине я могу просто получить доступ к значениям перечисления как глобальным, не нужно ::
Zebrafish
1
@TitoneMaurice: если у вас есть enum, вы не можете использовать ни область, ни глобальную область ( ::Saturday). Если у вас есть enum class(что совсем другое), то вы должны использовать Days::Saturday.
Mooing Duck
24

Этого будет достаточно, чтобы объявить переменную enum и сравнить ее:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Saturday;
if (day == Saturday) {
    std::cout << "Ok its Saturday";
}
mathematician1975
источник
почему неправильно говорить if (day == Days.Satudday)? они должны быть одинаковыми, так почему компилятор жалуется на это?
Рика
1
@Hossein значения, объявленные в вашем перечислении, не ведут себя как переменные-члены класса или структуры. Это не правильный синтаксис для использования
mathematician1975
2
@ Хоссейн: потому что Daysэто не сфера и не объект. Это тип. И сами типы не имеют членов. std::string.clearтакже не компилируется по той же причине.
Утка
8
@ Хоссейн: Потому что это не так, как работают перечисления в C ++. Перечисления с незаданной областью помещают свои значения в окружающее пространство имен; те из них enum class, которые находятся в области действия (впервые в 2011 г.) имеют собственную область видимости и доступны с помощью оператора области видимости Days::Saturday. Оператор доступа к членам ( .) используется только для доступа к членам класса.
Майк Сеймур
@MooingDUck и MikeSeymour Один из вас, ребята, опубликует свой ответ в качестве ответа? потому что это именно то, чего я добился, выпустив этот вопрос;)
Рика
22

Многое из этого должно дать вам ошибки компиляции.

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

Теперь Saturday, Sundayи т.д. могут быть использованы в качестве голых констант верхнего уровня, и Daysможет быть использован в качестве типа:

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

И аналогично позже, чтобы проверить:

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

Эти enumзначения , как голые константы - они ип -scoped - с небольшой дополнительной помощи со стороны компилятора: (если вы не используете ++ 11 C классов перечислений ) , они не инкапсулированы как объекта или структуры элементов , например, и вы не можете обратиться к ним в качестве членов в Days.

Вы получите то, что ищете, с C ++ 11 , который представляет enum class:

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

// ...

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

Обратите внимание, что этот C ++ немного отличается от C в двух отношениях, во-первых, C требует enumобъявления ключевого слова при объявлении переменной:

// day declaration in C:
enum Days day = Saturday;
pb2q
источник
Я обновил вопрос, я думаю, теперь стало понятнее, что именно я делаю :) Кстати, спасибо :)
Рика
14

Вы можете использовать трюк для использования областей по своему усмотрению, просто объявите enum следующим образом:

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

Days::type day = Days::Saturday;
if (day == Days::Saturday)
ataman1x
источник
9

Вместо того, чтобы использовать кучу операторов if, перечисления хорошо подходят для переключения операторов

В построителе уровней, которые я строю для своей игры, я использую несколько комбинаций enum / switch.

РЕДАКТИРОВАТЬ: Еще одна вещь, я вижу, вы хотите синтаксис похож на;

if(day == Days.Saturday)
etc

Вы можете сделать это в C ++:

if(day == Days::Saturday)
etc

Вот очень простой пример:

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;
}
Дин Найт
источник
Здесь хорошо то, что компиляторы сообщат вам, если вы пропустили ввод кейса.
Крис
Разве вы не должны использовать enum класса в этом случае?
Рика
1
enum - это просто тип данных в C ++. Поэтому объявление enum, как я делал выше, в файле .h, а затем включение этого файла в любой файл .cpp, в котором вы хотите его использовать, даст вам доступ к перечислению. Просто заметил, что я забыл добавить #include в мой пример .cpp. Редактирование.
Дин Найт
Кроме того, я вижу, что кто-то еще говорит, что перечисления в C ++ являются глобальными. По моему опыту, используя перечисления, как у меня выше, я могу получить к ним доступ только тогда, когда я включил .h. Так что, кажется, это также останавливает глобальный доступ, что всегда хорошо. РЕДАКТИРОВАТЬ: кажется, что я неосознанно использую перечисления в C ++ 11, если я правильно читаю ...
Дин Найт
9

Если вы все еще используете C ++ 03 и хотите использовать перечисления, вам следует использовать перечисления внутри пространства имен. Например:

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

Вы можете использовать enum вне пространства имен, например,

Daysofweek::Days day = Daysofweek::Saturday;

if (day == Daysofweek::Saturday)
{
    std::cout<<"Ok its Saturday";
}
Сан -
источник
8

Вы ищете строго типизированные перечисления , функция, доступная в стандарте C ++ 11 . Превращает перечисления в классы со значениями области видимости.

Используя ваш собственный пример кода, это:

  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

Использование в ::качестве средств доступа к перечислениям не удастся, если нацелено на стандарт C ++ до C ++ 11. Но некоторые старые компиляторы не поддерживают его, а некоторые IDE просто переопределяют эту опцию и устанавливают старый стандарт C ++.

Если вы используете GCC, включите C + 11 с -std = c ++ 11 или -std = gnu11 .

Будь счастлив!

Алекс Бирт
источник
1
Вы забыли написать enum class Days { ....
Мартин Хеннингс
На самом деле. исправить это! Спасибо.
Алекс Берт
7

Это не должно работать в C ++:

Days.Saturday

Days не является областью или объектом, который содержит членов, к которым вы можете получить доступ с помощью оператора точка. Этот синтаксис является просто C # -измом и недопустим в C ++.

Microsoft уже давно поддерживает расширение C ++, которое позволяет вам получать доступ к идентификаторам, используя оператор области действия:

enum E { A, B, C };

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

Но это нестандартно до C ++ 11. В C ++ 03 идентификаторы, объявленные в enum, существуют только в той же области видимости, что и сам тип enum.

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

C ++ 11 делает законным квалифицировать идентификаторы перечисления с именем перечисления, а также вводит классы перечисления, которые создают новую область видимости для идентификаторов вместо того, чтобы помещать их в окружающую область видимости.

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

enum class F { A, B, C };

A; // error
F::B;
bames53
источник
4

К сожалению, элементы перечисления являются «глобальными». Вы получаете доступ к ним, делая day = Saturday. Это означает, что вы не можете иметь, enum A { a, b } ;и enum B { b, a } ;они находятся в конфликте.

Grzegorz
источник
2
Пока вы не используете enum classв C ++ 11, то есть. Перед этим вы должны сделать фиктивные занятия.
Крис
Не знаю С ++ 11. Я предполагаю, что вопрос относится к C ++. Да, использование классов или пространств имен поможет.
Гжегож
@Grzegorz: я думаю, что Крис имеет в виду недавно введенный класс enum, который предоставляет строго типизированные перечисления.
Рика
@ Хоссейн: Спасибо, что указали на это. Я нашел объяснение классу num и знаю, о чем говорил Крис. Большое спасибо.
Гжегож
@Grzegorz: Я не хотел неуважения, просто подумал, что могу помочь, извините за возможные недоразумения. Еще раз спасибо за ваше время и помощь;)
Рика
4

Хотя C ++ (исключая C ++ 11) имеет перечисления, значения в них «просочились» в глобальное пространство имен.
Если вы не хотите, чтобы они просочились (и не НУЖНО использовать тип enum), подумайте о следующем:

class EnumName {  
   public:   
      static int EnumVal1;  
      (more definitions)  
};  
EnumName::EnumVal1 = {value};  
if ([your value] == EnumName::EnumVal1)  ...
InitializeSahib
источник
3

Перечисления в C ++ похожи на целые числа, маскируемые именами, которые вы даете им, когда вы объявляете свои значения перечислений (это не определение, а лишь подсказка, как это работает).

Но в вашем коде есть две ошибки:

  1. Записать enumвсе строчные
  2. Вам не нужно Days.до субботы.
  3. Если это перечисление объявлено в классе, используйте if (day == YourClass::Saturday){}
Balázs Édes
источник
ОП изменил орфографию / падеж через 16 минут после первоначального сообщения ( редакция 1 - редакция 2 ).
Питер Мортенсен
1

Я думаю, что вашей основной проблемой является использование .вместо ::, которое будет использовать пространство имен.

Пытаться:

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";
}
Джеймс Оравец
источник
Это не работает: чтобы использовать Days::область, как в вашем примере, вы должны определить перечисление enum class Daysи использовать расширение C ++ 03 + Microsoft или C ++ 11.
Футал
@Futal, вышесказанное работало с Borland C ++ Builder. Flavor / Версия C ++ не рассматривается.
Джеймс Оравец
1
ваша версия Borland C ++ Builder должна использовать C ++ 11 или новее. Gcc и Clang выдают ошибки или предупреждения, если ваш пример скомпилирован с помощью -std=c++98или -std=c++03. Лязг вполне понятно warning: use of enumeration in a nested name specifier is a C++11 extension.
Футал
1

Если мы хотим строгой безопасности типов и ограниченного перечисления, использование enum classхорошо в C ++ 11.

Если нам приходилось работать в C ++ 98, мы могли бы воспользоваться советом InitializeSahib, Sanданным для включения enum в области видимости.

Если мы также хотим строгой безопасности типов, следующий код может реализовать что-то вроде 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;
}

Код изменен из примера класса Месяц в книге Эффективное C ++ 3-е: Пункт 18

Сюй Хуэй
источник
-15

Прежде всего, сделайте «E» в enum, «e» в нижнем регистре.

Во-вторых, добавьте имя типа «Дни» в «Days.Saturday».

В-третьих ... купите себе хорошую книгу по С ++.

vikramjitSingh
источник
5
Извините, что вы получили все эти отрицательные голоса (я имею в виду, ответ действительно заслуживает этого), но это не значит, что вы должны покинуть сообщество на 6 лет. Вернись и присоединяйся к нам. Вы тоже можете внести свой вклад. Быть полезным. Поделитесь знаниями.
Габриэль Стейплс