Як використовувати перерахунки в 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 ++. Починаючи з «Енума».
Öö Tiib

1
@Hossein, оскільки перерахунки не є однаковим синтаксисом (і семантикою) в обох мовах. Перше, що я роблю після помилки при спробі використання функції новою мовою - це пошук синтаксису (або, якщо це можливо) на цій мові.
chris

@chris: Я знаю, я роблю те саме, що точно. Сподіваюся, я отримав свою відповідь. Я також оновив питання, щоб бути більш чітким. Дякую вам, до речі;)
Rika

17
" наскільки я знаю, декларація про перерахування та використання цих двох мов однакові. " Тут є ваша проблема. C # не є такою ж мовою, як C ++. Особливо вони мають різний синтаксис для переліків.
Robᵩ

Відповіді:


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, крім того, це маркер, а не оператор, і він показує точний символ, а не ім'я.
Mooing Duck

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

чому неправильно сказати, якщо (day == Days.Satudday)? вони повинні бути однаковими, тож чому компілятор скаржиться на це?
Ріка

1
@Hossein значення, оголошені у вашому перерахунку, не ведуть себе як змінні класу чи структури структури. Це не правильний синтаксис для використання
mathematician1975

2
@Hossein: тому що Daysце не сфера, ані об'єкт. Це тип. А самі типи не мають членів. std::string.clearтакож не вдається скласти з тієї ж причини.
Mooing Duck

8
@Hossein: Тому що це не так, як переживають 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значення схожі на голі константи - вони не зафіксовані - з невеликою додатковою допомогою компілятора: (якщо ви не використовуєте C ++ 11 enum-класів ), вони не інкапсульовані, як, наприклад, члени об'єкта чи структури, і ви не можете звернутися до них в якості членів в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;

Я оновив питання, я думаю, його тепер зрозуміліше, що я саме після :) До речі, дякую :)
Rika

14

Ви можете використовувати хитрість, щоб використовувати сфери, як хочете, просто оголосити перерахунок таким чином:

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

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

9

Замість того, щоб використовувати купу if-висловлювань, перелічувачі добре піддаються переключенню операцій

Я використовую деякі комбінації перемикань / перемикань у розробнику рівнів, який будую для своєї гри.

EDIT: Інша річ, я бачу, вам потрібен синтаксис, схожий на;

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

Приємно, що компілятори скажуть вам, чи не пропустили ви справу.
chris

Чи не слід в цьому випадку використовувати enum class?
Ріка

1
enum - це лише тип даних у C ++. Отже, оголошення enum, як я робив вище, у .h-файлі, а потім включення цього файлу у будь-який .cpp-файл, у якому ви хочете його використовувати, надасть вам доступ до enum. Щойно помітив, я забув додати #include у своєму прикладі .cpp. Редагування.
Дін Лицай

Також я бачу, що хтось ще каже, що перерахунки на C ++ є глобальними. З мого досвіду, використовуючи enums, як я маю вище, я можу отримати доступ до них лише тоді, коли я включив .h. Тож це, здається, також зупиняє глобальний доступ, що завжди добре. EDIT: Схоже, я несвідомо використовую перерахунки способом 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 ++ std.

Якщо ви використовуєте GCC, увімкніть C + 11 за допомогою -std = c ++ 11 або -std = gnu11 .

Будь щасливий!


1
Ви забули написати enum class Days { ....
Мартін Хеннінгс

Справді. виправлення! Дякую.
Alex Byrth

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 робить законним кваліфікувати ідентифікатори enum з ім'ям enum, а також вводить enum-класи, які створюють нову область для ідентифікаторів замість того, щоб розміщувати їх у навколишньому просторі.

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, тобто. Перед цим ви повинні зробити уроки манекенів.
chris

Не знаю С ++ 11. Я припускаю, що питання стосується C ++. Так, використання класів або просторів імен зробить свою справу.
Гжегож

@Grzegorz: Я думаю, що Кріс має на увазі щойно представлений клас перерахунків, який забезпечує сильно набрані перерахунки.
Ріка

@Hossein: Дякую, що ви вказали на це. Я знайшов пояснення класу num, і я знаю, про що Кріс говорив. Дуже дякую.
Гжегож

@Grzegorz: Я не мав на увазі неповаги, просто думав, що можу допомогти, вибачте за будь-яке ймовірне непорозуміння. Знову дякую за ваш час і допомагаєте мені;)
Rika

4

Хоча C ++ (крім C ++ 11) має перерахунки, значення в них "просочуються" у глобальний простір імен.
Якщо ви не хочете, щоб вони просочилися (і НЕ ПОТРІБНО використовувати тип enum), врахуйте наступне:

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

3

Енуми в C ++ - це як цілі числа, замасковані іменами, які ви їм даєте, коли ви оголошуєте свої enum-значення (це не визначення, а лише натяк на те, як це працює).

Але у вашому коді є дві помилки:

  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. Смак / версія C ++ не в питанні.
Джеймс Оравек

1
Ваша версія програми Borland C ++ Builder повинна використовувати C ++ 11 або новішу версію. Gcc та Clang видають помилки або попередження, якщо ваш приклад складено з -std=c++98або -std=c++03. Clang абсолютно ясно: warning: use of enumeration in a nested name specifier is a C++11 extension.
Футал

1

Якщо ми хочемо, щоб суворий тип безпеки та обширний перелік були використані, enum classце добре в С ++ 11.

Якщо нам довелося працювати в C ++ 98, ми можемо, скориставшись порадами InitializeSahib, Sanщоб увімкнути обсяг перерахунку.

Якщо ми також хочемо суворої безпеки безпеки, наступний код може реалізувати щось подібне 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" у "Days.Saturday".

По-третє ... придбайте собі гарну книгу на C ++.


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