C ++ 11'de bir enum sınıfının değerini nasıl çıkarabilirim


102

enum classC ++ 11'de an'ın değerini nasıl çıkarabilirim ? C ++ 03'te şöyle:

#include <iostream>

using namespace std;

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

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

c ++ 0x'de bu kod derlenmez

#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'da derlendi


1
Neden enum çıktısını almaya çalışıyorsunuz? enum sınıfı, enum değerlerini int gösterimi ile karıştırmamak için kullanılır
RiaD

Yanıtlar:


129

Kapsamlı bir numaralandırmanın aksine, kapsamlı bir numaralandırma örtük olarak tamsayı değerine dönüştürülemez. Buna gerek açıkça bir döküm kullanarak bir tam sayıya dönüştürmek:

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

Mantığı bir işlev şablonuna kapsüllemek isteyebilirsiniz:

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

olarak kullanılır:

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

3
Bunun sondaki dönüş türü sözdizimini kullanmasının bir nedeni var mı?
Nicol Bolas

3
@NicolBolas: as_integerAçık kaynak kitaplıklarımdan biri olan CxxReflect'ten kopyaladım (bkz. Enumeration.hpp ). Kitaplık her yerde tutarlı bir şekilde takip eden dönüş türlerini kullanır. Tutarlılık için.
James McNellis

11
Bu 2 yıl geç olmasına rağmen, bir başkasının bu soruyu görmesi durumunda yukarıdaki cast tekniğini kullanabilir ve tamsayıyı veya "static_cast <A> (intValue)" değerini almak için sadece "static_cast <int> (değer)" çağırabilirsiniz. bir enum değeri alın. İnt'ten enum'a veya enum'dan enum'a geçmenin sorunlara neden olabileceğini ve genellikle bir tasarım hatasının işareti olduğunu unutmayın.
Benjamin Danger Johnson

4
int (değer) ve A (intValue) da çirkin açılı parantezler olmadan çalışır.
Grault

4
as_integerconstexprsabit ifadenin gerekli olduğu bağlamlarda kullanılabilecek şekilde tanımlanabilir .
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;
}

Bu örneği aynen kopyaladım ve olduğu gibi derledim g++ -std=c++0x enum.cppama bir sürü derleyici hatası alıyorum -> pastebin.com/JAtLXan9 . Ayrıca @ james-mcnellis'ten derlemek için örnek alamadım.
Dennis

4
@Dennis underlying_type sadece C ++ 11 olduğunu
Deqing

23

İkinci örneğinizin (yani, kapsamlı bir enum kullanan) kapsamsız numaralandırmalarla aynı sözdizimini kullanarak çalışması mümkündür. Ayrıca, çözelti genel olduğunu ve (gösterildiği gibi, her kapsamına sahip enum için kod yazma karşı, her kapsamlı enums için çalışacaktır yanıt sağladığı @ForEveR ).

Çözüm, operator<<herhangi bir kapsamlı enum için çalışacak genel bir işlev yazmaktır . Çözüm, SFINAE aracılığıyla kullanır std::enable_ifve aşağıdaki gibidir.

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

Bir ihtiyaç typenameönce std::underlying_type<T>::type.
uckelman

@uckelman Kesinlikle haklısın. Cevabımı güncellediğiniz için teşekkürler.
James Adkison

bu benim için clang altında çalıştı, ancak gcc 4.9.2 altında, bu çözüm hata ile << birlikte zincirlendiğinde başarısız oluyor error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’. bunun nedeni akış geçici olduğunda ADL'nin başarısız olması ve yukarıdaki şablonun bir olasılık olmamasıdır. herhangi bir ipucu?
ofloveandhate

@ofloveandhate Sorunu üreten bir örneğe bağlantı verebilir misiniz? Yukarıdaki kodu gcc 4.9.2'de sorunsuz bir şekilde test ettim ve sadece küçük bir değişiklik yaptım , operatörleri birbirine zincirleyerek 3 coutifadeyi tek bir ifadeye dönüştürdüm . Buraya bakıncout<<
James Adkison

ifademi gözden geçirmeme izin verin. Bir sınıfın içinde bulunan bir enum sınıfını o sınıfın dışından yazdırmaya çalışıyordum. Yukarıdaki kod aslında bir sınıfın kendi içinde yer almayan enum sınıfları için çalışır.
ofloveandhate

10

(Henüz yorum yapmama izin verilmiyor.) James McNellis'in halihazırda harika olan cevabına aşağıdaki iyileştirmeleri öneririm:

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

ile

  • constexpr: derleme zamanı dizi boyutu olarak bir enum üye değeri kullanmama izin veriyor
  • static_assert+ is_enum: fonksiyonun derleme zamanını 'sağlamak' için. önerildiği gibi yalnızca numaralandırmalarla

Bu arada kendime soruyorum: enum classEnum üyelerime sayı değerleri atamak istediğimde neden kullanmalıyım ?! Dönüşüm çabasını göz önünde bulundurarak.

Belki daha sonra enumburada önerdiğim gibi sıradanlığa geri dönerim : Numaralandırmaları C ++ 'da bayrak olarak nasıl kullanabilirim?


Bir @TobySpeight önerisine dayanarak, static_assert içermeyen bir başka (daha iyi) çeşidi:

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

Var Tolan std::underlying_type<T>::typeama std::is_enum<T>::valueyanlış olan bir tür var mı? Aksi takdirde, static_asserthiçbir değer katmaz.
Toby Speight

1
Tüm derleyiciler üzerinde test etmedim. Ancak, @TobySpeight muhtemelen haklısınız, msvc2013, underlying_type_t ile türün kendisinin enum olması arasında 1'e 1 bir yazışma öneren anlaşılır hata mesajları veriyor gibi görünüyor. Ve static_assert bile ateşlenmedi. Ancak: referans, tam bir enum türü ile sağlanmadıysa temel_tür davranışının tanımsız olduğunu söylüyor. Dolayısıyla static_assert, her durumda maksimum anlaşılır bir mesaj almak için bir umuttur. Belki daha erken / en erken işleme alınmasını zorlama olasılıkları vardır?
yau

Ah evet, Enumerationtam bir enum türü değilse tanımsız olduğu konusunda haklısınız . Bu durumda, dönüş türünde kullanıldığı için çok geç olabilir. Belki std::enable_if<std::is_enum<Enumeration>::value, std::underlying_type<Enumeration>::type>dönüş türü olarak belirtebiliriz ? Elbette, Kavramları destekleyen bir derleyiciniz varsa, çok daha kolay (ve hata mesajları çok daha net) ...
Toby Speight

8

Daha basit yazmak için,

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

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

Bu, numaralandırmaya açıkça temel bir tür verildiğinde işe yaramaz
James

3

Aşağıdakiler benim için C ++ 11'de çalıştı:

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

Bunun gibi bir şey yapabilirsiniz:

//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
Soru bir enum sınıfı hakkında soruldu.
Karınca
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.