Как использовать перечисления в 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)

?

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


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";
}

почему неправильно говорить 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;

Я обновил вопрос, я думаю, теперь стало понятнее, что именно я делаю :) Кстати, спасибо :)
Рика

14

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

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

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

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;

4

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


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)  ...

3

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

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

  1. Записать enumвсе строчные
  2. Вам не нужно Days.до субботы.
  3. Если это перечисление объявлено в классе, используйте if (day == YourClass::Saturday){}

ОП изменил орфографию / падеж через 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».

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


5
Извините, что вы получили все эти отрицательные голоса (я имею в виду, ответ действительно заслуживает этого), но это не значит, что вы должны покинуть сообщество на 6 лет. Вернись и присоединяйся к нам. Вы тоже можете внести свой вклад. Быть полезным. Поделитесь знаниями.
Габриэль Стейплс
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.