Bagaimana cara menampilkan nilai kelas enum di C ++ 11

105

Bagaimana cara mengeluarkan nilai dari enum classdalam C ++ 11? Di C ++ 03 ini seperti ini:

#include <iostream>

using namespace std;

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

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

di c ++ 0x kode ini tidak dapat dikompilasi

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

disusun di Ideone.com

Adi
sumber
1
Mengapa Anda mencoba mengeluarkan enum? kelas enum digunakan untuk tidak mencampur nilai enum dengan representasi int
RiaD

Jawaban:

133

Tidak seperti enumerasi tanpa cakupan, enumerasi terbatas tidak secara implisit dapat dikonversi menjadi nilai integernya . Anda perlu secara eksplisit mengubahnya menjadi integer menggunakan cast:

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

Anda mungkin ingin merangkum logika ke dalam template fungsi:

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

digunakan sebagai:

std::cout << as_integer(a) << std::endl;
James McNellis
sumber
3
Apakah ada alasan mengapa ini menggunakan sintaks tipe kembalian jejak?
Nicol Bolas
3
@NicolBolas: Saya menyalin as_integerdari salah satu perpustakaan open-source saya, CxxReflect (lihat enumeration.hpp ). Pustaka menggunakan tipe pengembalian jejak secara konsisten, di mana-mana. Untuk konsistensi.
James McNellis
11
Meskipun ini terlambat 2 tahun, jika orang lain melihat pertanyaan ini, Anda dapat menggunakan metode teknik cast di atas dan cukup memanggil "static_cast <int> (nilai)" untuk mendapatkan integer atau "static_cast <A> (intValue)" ke dapatkan nilai enum. Perlu diingat bahwa beralih dari int ke enum atau enum ke enum dapat menyebabkan masalah dan umumnya merupakan tanda bug desain.
Benjamin Danger Johnson
4
int (value) dan A (intValue) juga berfungsi, tanpa tanda kurung sudut jelek.
Grault
4
as_integerdapat didefinisikan constexprsedemikian rupa sehingga dapat digunakan dalam konteks di mana ekspresi konstan diperlukan.
Nawaz
40
#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;
}
Selama-lamanya
sumber
Saya menyalin contoh ini secara verbatim dan mengkompilasinya seperti g++ -std=c++0x enum.cpptetapi saya mendapatkan banyak kesalahan kompiler -> pastebin.com/JAtLXan9 . Saya juga tidak bisa mendapatkan contoh dari @ james-mcnellis untuk dikompilasi.
Dennis
4
@Dennis yang mendasari jenis hanya di C ++ 11
Deqing
23

Dimungkinkan untuk mendapatkan contoh kedua Anda (yaitu, yang menggunakan enum terbatas) untuk bekerja menggunakan sintaks yang sama seperti enum tanpa batas. Selain itu, solusinya bersifat generik dan akan berfungsi untuk semua enum cakupan, dibandingkan menulis kode untuk setiap enum cakupan (seperti yang ditunjukkan dalam jawaban yang disediakan oleh @ForEveR ).

Solusinya adalah menulis operator<<fungsi generik yang akan berfungsi untuk enum cakupan apa pun. Solusinya menggunakan SFINAE via std::enable_ifdan adalah sebagai berikut.

#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;
}
James Adkison
sumber
Anda membutuhkan typenamesebelumnya std::underlying_type<T>::type.
uckelman
@uckel Anda benar sekali. Terima kasih telah memperbarui jawaban saya.
James Adkison
ini bekerja untuk saya di bawah dentang, tetapi di bawah gcc 4.9.2, solusi ini gagal saat merangkai << bersama-sama, dengan kesalahan error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’. ini tampaknya karena ketika streaming bersifat sementara, ADL gagal, dan template di atas tidak memungkinkan. ada tips?
ofloveandhate
@ofloveandhate Dapatkah Anda memberikan tautan ke contoh yang menghasilkan masalah? Saya menguji kode di atas di gcc 4.9.2 tanpa masalah dan hanya sedikit perubahan, saya mengubah 3 coutpernyataan menjadi satu coutpernyataan dengan merangkai <<operator bersama. Lihat di sini
James Adkison
izinkan saya merevisi pernyataan saya. Saya mencoba untuk mencetak kelas enum yang terdapat di dalam kelas, dari luar kelas itu. kode di atas memang berfungsi untuk kelas enum yang tidak terdapat dalam kelas itu sendiri.
ofloveandhate
10

(Saya belum diizinkan untuk berkomentar.) Saya akan menyarankan perbaikan berikut untuk jawaban James McNellis yang sudah hebat:

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

dengan

  • constexpr: memungkinkan saya untuk menggunakan nilai anggota enum sebagai ukuran array waktu kompilasi
  • static_assert+ is_enum: untuk 'memastikan' waktu kompilasi bahwa fungsi melakukan sth. dengan pencacahan saja, seperti yang disarankan

Ngomong-ngomong, saya bertanya pada diri sendiri: Mengapa saya harus menggunakan enum classsaat saya ingin menetapkan nilai angka ke anggota enum saya ?! Mempertimbangkan upaya konversi.

Mungkin saya kemudian akan kembali ke biasa enumseperti yang saya sarankan di sini: Bagaimana menggunakan enums sebagai flag di C ++?


Namun rasa lain (lebih baik) tanpa static_assert, berdasarkan saran dari @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);
}
yau
sumber
Apakah ada tipe Tyang std::underlying_type<T>::typeada, tapi std::is_enum<T>::valuesalah? Jika tidak, maka static_asserttidak ada nilai tambah.
Toby Speight
1
Saya tidak menguji semua kompiler. Tapi, @TobySpeight Anda mungkin benar, msvc2013 tampaknya mengeluarkan pesan kesalahan yang dapat dipahami, menyarankan korespondensi 1-ke-1 antara underlying_type_t existing dan tipe itu sendiri menjadi enum. Dan static_assert bahkan tidak diaktifkan. Tapi: referensi mengatakan bahwa perilaku underlying_type tidak ditentukan jika tidak disediakan dengan tipe enum lengkap. Jadi static_assert hanyalah harapan untuk mendapatkan pesan yang dapat dipahami secara maksimal. Mungkin ada kemungkinan untuk memaksanya diproses lebih awal / paling awal?
yau
Ah ya, Anda benar bahwa itu tidak ditentukan jika Enumerationbukan tipe enum lengkap. Dalam hal ini, mungkin sudah terlambat, karena digunakan dalam tipe pengembalian. Mungkin kita bisa menentukan std::enable_if<std::is_enum<Enumeration>::value, std::underlying_type<Enumeration>::type>sebagai tipe pengembalian? Tentu saja, ini jauh lebih mudah (dan pesan kesalahan jauh lebih jelas) jika Anda memiliki kompiler dengan dukungan untuk Konsep ...
Toby Speight
9

Untuk menulis lebih sederhana,

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

int value = static_cast<int>(Color::Blue); // 111
Audrius Meskauskas
sumber
Ini tidak akan berfungsi ketika enum secara eksplisit diberikan tipe yang mendasari
Yakobus
3

Berikut ini berhasil untuk saya di 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);
}
Alat pemecah buah keras
sumber
0

Anda bisa melakukan sesuatu seperti ini:

//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;
Earl of Lemongrab
sumber
2
Pertanyaan yang diajukan tentang kelas enum.
Semut