Як я можу вивести значення класу переліку в C ++ 11


96

Як я можу вивести значення an enum classу C ++ 11? У C ++ 03 це так:

#include <iostream>

using namespace std;

enum A {
  a = 1,
  b = 69,
  c= 666
};

int main () {
  A a = A::c;
  cout << a << endl;
}

в c ++ 0x цей код не компілюється

#include <iostream>

using namespace std;

enum class A {
  a = 1,
  b = 69,
  c= 666
};

int main () {
  A a = A::c;
  cout << a << endl;
}


prog.cpp:13:11: error: cannot bind 'std::ostream' lvalue to 'std::basic_ostream<char>&&'
/usr/lib/gcc/i686-pc-linux-gnu/4.5.1/../../../../include/c++/4.5.1/ostream:579:5: error:   initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char, _Traits = std::char_traits<char>, _Tp = A]'

складений на Ideone.com


1
Чому ви намагаєтесь вивести перелічення? клас enum використовується, щоб не змішувати значення
— переліку

Відповіді:


122

На відміну від нескопірованого перерахування, обчислюване перерахування не є неявно конвертованим до цілочисельного значення. Вам потрібно явно перетворити його на ціле число, використовуючи привід:

std::cout << static_cast<std::underlying_type<A>::type>(a) << std::endl;

Можливо, ви захочете інкапсулювати логіку у шаблон функції:

template <typename Enumeration>
auto as_integer(Enumeration const value)
    -> typename std::underlying_type<Enumeration>::type
{
    return static_cast<typename std::underlying_type<Enumeration>::type>(value);
}

використовується як:

std::cout << as_integer(a) << std::endl;

3
Чи є причина, за якою використовується синтаксис типу повернення?
— Nicol Bolas,

3
@NicolBolas: Я скопіював as_integerз одного з моїх відкритих джерел бібліотек, CxxReflect (див enumeration.hpp ). Бібліотека використовує кінцеві типи повернення послідовно, скрізь. Для послідовності.
— James McNellis

11
Незважаючи на те, що це запізнилося на 2 роки, якщо хтось інший побачить це питання, ви можете просто скористатися наведеним вище методом приведення і просто викликати "static_cast <int> (value)", щоб отримати ціле число або "static_cast <A> (intValue)", щоб отримати значення переліку. Тільки майте на увазі, що перехід від int до enum або enum до enum може спричинити проблеми, і, як правило, це ознака помилки дизайну.
— Бенджамін Денджер Джонсон,

4
int (value) та A (intValue) також працюють, без потворних кутових дужок.
— Grault

4
as_integerможна визначити constexprтаким чином, щоб його можна було використовувати в контексті, де необхідний постійний вираз.
— Наваз,

39
#include <iostream>
#include <type_traits>

using namespace std;

enum class A {
  a = 1,
  b = 69,
  c= 666
};

std::ostream& operator << (std::ostream& os, const A& obj)
{
   os << static_cast<std::underlying_type<A>::type>(obj);
   return os;
}

int main () {
  A a = A::c;
  cout << a << endl;
}

Я скопіював цей приклад дослівно і скомпілював його як, g++ -std=c++0x enum.cppале я отримую купу помилок компілятора -> pastebin.com/JAtLXan9 . Я також не зміг отримати приклад від @ james-mcnellis для компіляції.
— Денніс,

4
@Dennis underlying_type тільки в C ++ 11
— Deqing

23

Можна отримати свій другий приклад (тобто той, що використовує обчислювальне перерахування) для роботи, використовуючи той самий синтаксис, що і необмежені перерахування. Крім того, рішення є загальним і буде працювати для всіх перелічених областей, у порівнянні із написанням коду для кожного переліченого переліку (як показано у відповіді, наданому @ForEveR ).

Рішення полягає в тому, щоб написати загальну operator<<функцію, яка працюватиме для будь-якого обчислюваного переліку. Рішення використовує SFINAE через std::enable_ifта таке.

#include <iostream>
#include <type_traits>

// Scoped enum
enum class Color
{
    Red,
    Green,
    Blue
};

// Unscoped enum
enum Orientation
{
    Horizontal,
    Vertical
};

// Another scoped enum
enum class ExecStatus
{
    Idle,
    Started,
    Running
};

template<typename T>
std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::ostream>::type& stream, const T& e)
{
    return stream << static_cast<typename std::underlying_type<T>::type>(e);
}

int main()
{
    std::cout << Color::Blue << "\n";
    std::cout << Vertical << "\n";
    std::cout << ExecStatus::Running << "\n";
    return 0;
}

Вам потрібно typenameдо std::underlying_type<T>::type.
— uckelman

@uckelman Ви абсолютно праві. Дякуємо за оновлення моєї відповіді.
— Джеймс Адкісон,

це спрацювало для мене під clang, але під gcc 4.9.2 це рішення не вдається, коли ланцюжок << разом, з помилкою error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’. це, здається, тому, що коли потік є тимчасовим, ADL не працює, і наведений вище шаблон не є можливою. будь-які поради?
— ofloveandhate

@ofloveandhate Не могли б ви надати посилання на приклад, що видає проблему? Я тестував вище код в GCC 4.9.2 без будь - яких проблем , і лише незначна зміна, я перетворив 3 coutзаяви в одному coutзаяві, прикувавши до <<операторам разом. Дивіться тут
— Джеймс Адкісон

дозвольте мені переглянути свою заяву. Я намагався надрукувати клас enum, що міститься всередині класу, поза цим класом. наведений вище код справді працює для класів перечислення, які не містяться в самому класі.
— ofloveandhate

10

(Мені поки що не дозволяють коментувати.) Я б запропонував наступні вдосконалення і без того чудової відповіді Джеймса МакНелліса:

template <typename Enumeration>
constexpr auto as_integer(Enumeration const value)
    -> typename std::underlying_type<Enumeration>::type
{
    static_assert(std::is_enum<Enumeration>::value, "parameter is not of type enum or enum class");
    return static_cast<typename std::underlying_type<Enumeration>::type>(value);
}

з

  • constexpr: дозволяє мені використовувати значення члена enum як розмір масиву під час компіляції
  • static_assert+ is_enum: "забезпечити" час компіляції, що функція виконує sth. лише з переліками, як пропонується

До речі, я запитую себе: навіщо мені коли-небудь використовувати, enum classколи я хотів би присвоїти числові значення своїм членам перерахування ?! Враховуючи зусилля щодо перетворення.

Можливо, я б тоді повернувся до звичайного, enumяк я запропонував тут: Як використовувати перелічення як прапори в C ++?


Ще один (кращий) його смак без static_assert, заснований на пропозиції @TobySpeight:

template <typename Enumeration>
constexpr std::enable_if_t<std::is_enum<Enumeration>::value,
std::underlying_type_t<Enumeration>> as_number(const Enumeration value)
{
    return static_cast<std::underlying_type_t<Enumeration>>(value);
}

Чи існує тип, Tдля якого std::underlying_type<T>::typeіснує, але std::is_enum<T>::valueє помилковим? Якщо ні, то static_assertдодана вартість не додає.
— Тобі Спейт

1
Я не тестував на всіх компіляторах. Але, @TobySpeight, ви, мабуть, маєте рацію, msvc2013, схоже, видає зрозумілі повідомлення про помилки, пропонуючи відповідність 1 до 1 між underlying_type_t і типом, що перераховується. А static_assert навіть не запускається. Але: посилання говорить, що поведінка underlying_type є невизначеною, якщо не надається повний тип перечислення. Тож static_assert - це просто надія отримати максимально зрозуміле повідомлення на випадок. Можливо, є можливості змусити його оброблятись раніше / раніше?
— yau

Ах так, ви маєте рацію, що це невизначено, якщо Enumerationце не повний тип переліку. У цьому випадку це може бути вже занадто пізно, оскільки воно використовується у типі повернення. Можливо, ми могли б вказати std::enable_if<std::is_enum<Enumeration>::value, std::underlying_type<Enumeration>::type>як тип повернення? Звичайно, набагато простіше (і повідомлення про помилки набагато чіткіше), якщо у вас є компілятор з підтримкою концепцій ...
— Тобі Спейт

6

Щоб писати простіше,

enum class Color
{
    Red = 1,
    Green = 11,
    Blue = 111
};

int value = static_cast<int>(Color::Blue); // 111

Це не спрацює, коли перерахуванню явно присвоєно базовий тип
— Джеймс,

3

Для мене в С ++ 11 працювало наступне:

template <typename Enum>
constexpr typename std::enable_if<std::is_enum<Enum>::value,
                                  typename std::underlying_type<Enum>::type>::type
to_integral(Enum const& value) {
    return static_cast<typename std::underlying_type<Enum>::type>(value);
}

0

Ви можете зробити щось подібне:

//outside of main
namespace A
{
    enum A
    {
        a = 0,
        b = 69,
        c = 666
    };
};

//in main:

A::A a = A::c;
std::cout << a << std::endl;

2
Запитання про клас переліку.
— Мураха
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.