Як я можу вивести значення класу переліку в 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.