Làm cách nào để xuất giá trị của một lớp enum trong C ++ 11


96

Làm cách nào để xuất giá trị của an enum classtrong C ++ 11? Trong C ++ 03 nó như thế này:

#include <iostream>

using namespace std;

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

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

trong c ++ 0x mã này không biên dịch

#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]'

tổng hợp tại Ideone.com


1
Tại sao bạn đang cố gắng xuất ra enum? lớp enum được sử dụng để không nhầm lẫn giữa các giá trị enum với đại diện int
Riad

Câu trả lời:


122

Không giống như kiểu liệt kê không theo dõi, kiểu liệt kê có phạm vi không hoàn toàn có thể chuyển đổi thành giá trị nguyên của nó. Bạn cần chuyển đổi nó thành một số nguyên một cách rõ ràng bằng cách sử dụng một kiểu:

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

Bạn có thể muốn đóng gói logic vào một mẫu hàm:

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

được dùng như:

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

3
Có lý do gì mà điều này sử dụng cú pháp kiểu trả về theo sau không?
Nicol Bolas

3
@NicolBolas: Tôi đã sao chép as_integertừ một trong các thư viện nguồn mở của mình, CxxReflect (xem enumeration.hpp ). Thư viện sử dụng các kiểu trả về theo sau một cách nhất quán ở mọi nơi. Cho thống nhất.
James McNellis

11
Mặc dù đã trễ 2 năm nhưng trong trường hợp người khác nhìn thấy câu hỏi này, bạn có thể sử dụng phương pháp ép kiểu ở trên và chỉ cần gọi "static_cast <int> (value)" để lấy số nguyên hoặc "static_cast <A> (intValue)" để nhận một giá trị enum. Chỉ cần lưu ý rằng chuyển từ int sang enum hoặc enum sang enum có thể gây ra sự cố và nói chung là dấu hiệu của lỗi thiết kế.
Benjamin Danger Johnson

4
int (value) và A (intValue) cũng hoạt động mà không có dấu ngoặc nhọn xấu xí.
Grault

4
as_integercó thể được định nghĩa để constexprnó có thể được sử dụng trong các ngữ cảnh cần biểu thức hằng số.
Nawaz

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

Tôi đã sao chép nguyên văn ví dụ này và biên dịch nó g++ -std=c++0x enum.cppnhưng tôi đang gặp một loạt lỗi trình biên dịch -> pastebin.com/JAtLXan9 . Tôi cũng không thể lấy ví dụ từ @ james-mcnellis để biên dịch.
Dennis

4
@Dennis underlying_type chỉ có trong C ++ 11
Deqing

23

Có thể lấy ví dụ thứ hai của bạn (ví dụ: ví dụ sử dụng một enum có phạm vi) hoạt động bằng cách sử dụng cùng một cú pháp với enum chưa được mở. Hơn nữa, giải pháp là chung và sẽ hoạt động cho tất cả các enum có phạm vi, so với việc viết mã cho mỗi enum trong phạm vi (như được hiển thị trong câu trả lời được cung cấp bởi @ForEveR ).

Giải pháp là viết một operator<<hàm chung sẽ hoạt động cho bất kỳ enum có phạm vi nào. Giải pháp sử dụng SFINAE thông quastd::enable_if và như sau.

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

Bạn cần một typenametrước std::underlying_type<T>::type.
uckelman

@uckelman Bạn hoàn toàn chính xác. Cảm ơn đã cập nhật câu trả lời của tôi.
James Adkison

điều này đã làm việc cho tôi theo tiếng kêu, nhưng trong gcc 4.9.2, giải pháp này không thành công khi chuỗi << với nhau, với lỗi error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’. điều này có vẻ là do khi luồng tạm thời, ADL không thành công và mẫu trên không có khả năng xảy ra. bất kỳ lời khuyên?
ofloveandhate

@ofloveandhate Bạn có thể cung cấp liên kết đến một ví dụ gây ra sự cố không? Tôi đã thử nghiệm đoạn mã trên trong gcc 4.9.2 mà không gặp bất kỳ sự cố nào và chỉ có một chút thay đổi, tôi đã chuyển đổi 3 coutcâu lệnh thành một coutcâu lệnh duy nhất bằng cách xâu chuỗi các <<toán tử lại với nhau. Xem ở đây
James Adkison

hãy để tôi sửa đổi tuyên bố của tôi. Tôi đang cố gắng in một lớp enum chứa bên trong một lớp, từ bên ngoài lớp đó. đoạn mã trên thực sự hoạt động đối với các lớp enum không chứa trong chính lớp đó.
ofloveandhate

10

(Tôi chưa được phép bình luận.) Tôi sẽ đề xuất những cải tiến sau cho câu trả lời vốn đã tuyệt vời của James McNellis:

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

với

  • constexpr: cho phép tôi sử dụng giá trị thành viên enum làm kích thước mảng thời gian biên dịch
  • static_assert+ is_enum: để 'đảm bảo' thời gian biên dịch mà hàm thực hiện thứ. chỉ với liệt kê, như được đề xuất

Nhân tiện, tôi tự hỏi mình: Tại sao tôi nên sử dụng enum class khi tôi muốn gán giá trị số cho các thành viên enum của mình ?! Xem xét nỗ lực chuyển đổi.

Có lẽ sau đó tôi sẽ trở lại bình thường enumnhư tôi đã đề xuất ở đây: Làm thế nào để sử dụng enum làm cờ trong C ++?


Tuy nhiên, một hương vị khác (tốt hơn) của nó mà không có static_assert, dựa trên đề xuất của @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);
}

Có loại Tnào std::underlying_type<T>::typetồn tại nhưng std::is_enum<T>::valuelà sai không? Nếu không, thì static_assertgiá trị không tăng thêm.
Toby Speight

1
Tôi đã không thử nghiệm trên tất cả các trình biên dịch. Tuy nhiên, @TobySpeight bạn có thể đúng, msvc2013 dường như đưa ra các thông báo lỗi dễ hiểu, đề xuất sự tương ứng 1-1 giữa underlying_type_t hiện có và bản thân loại là enum. Và static_assert thậm chí không được kích hoạt. Nhưng: tham chiếu nói rằng hành vi của underlying_type là không xác định nếu không được cung cấp một kiểu enum hoàn chỉnh. Vì vậy, static_assert chỉ là một hy vọng để nhận được một thông báo có thể hiểu được tối đa trong trường hợp. Có lẽ có khả năng buộc nó phải được xử lý sớm hơn / sớm nhất?
yau

À vâng, bạn nói đúng rằng nó không được xác định nếu Enumerationkhông phải là một kiểu enum hoàn chỉnh. Trong trường hợp đó, có thể đã quá muộn, vì nó được sử dụng trong kiểu trả về. Có lẽ chúng ta có thể chỉ định std::enable_if<std::is_enum<Enumeration>::value, std::underlying_type<Enumeration>::type>là kiểu trả về? Tất nhiên, nó rất dễ dàng hơn nhiều (và các thông báo lỗi nên rõ ràng hơn nhiều) nếu bạn có một trình biên dịch với sự hỗ trợ cho khái niệm ...
Toby Speight

7

Để viết đơn giản hơn,

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

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

Điều này sẽ không hoạt động khi enum được đưa ra một cách rõ ràng một loại tiềm ẩn
James

3

Sau đây đã làm việc cho tôi trong C ++ 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

Bạn có thể làm điều gì đó như sau:

//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
Câu hỏi hỏi về một lớp enum.
Ant
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.