Jak mogę wyprowadzić wartość an enum class
w C ++ 11? W C ++ 03 wygląda to tak:
#include <iostream>
using namespace std;
enum A {
a = 1,
b = 69,
c= 666
};
int main () {
A a = A::c;
cout << a << endl;
}
w c ++ 0x ten kod nie kompiluje się
#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]'
opracowane na Ideone.com
Odpowiedzi:
W przeciwieństwie do wyliczenia bez zakresu, wyliczenie w zakresie nie jest niejawnie konwertowane na jego wartość całkowitą. Musisz jawnie przekonwertować go na liczbę całkowitą za pomocą rzutowania:
std::cout << static_cast<std::underlying_type<A>::type>(a) << std::endl;
Możesz chcieć hermetyzować logikę w szablonie funkcji:
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); }
użyty jako:
std::cout << as_integer(a) << std::endl;
źródło
as_integer
z jednej z moich bibliotek open source, CxxReflect (patrz enumeration.hpp ). Biblioteka konsekwentnie używa końcowych typów zwracanych wszędzie. Dla spójności.as_integer
można zdefiniowaćconstexpr
tak, aby można go było używać w kontekstach, w których potrzebne jest stałe wyrażenie.#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; }
źródło
g++ -std=c++0x enum.cpp
ale otrzymuję kilka błędów kompilatora -> pastebin.com/JAtLXan9 . Nie mogłem również pobrać przykładu z @ james-mcnellis do skompilowania.Możliwe jest uzyskanie drugiego przykładu (tj. Tego, który używa wyliczenia o określonym zakresie), aby działał przy użyciu tej samej składni, co wyliczenia bez zakresu. Ponadto rozwiązanie jest ogólne i będzie działać dla wszystkich wyliczeń o określonym zakresie, w przeciwieństwie do pisania kodu dla każdego wyliczenia w zakresie (jak pokazano w odpowiedzi dostarczonej przez @ForEveR ).
Rozwiązaniem jest napisanie
operator<<
funkcji ogólnej, która będzie działać dla każdego wyliczenia o określonym zakresie. Rozwiązanie wykorzystuje SFINAE viastd::enable_if
i wygląda następująco.#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; }
źródło
typename
wcześniejstd::underlying_type<T>::type
.error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’
. wydaje się, że dzieje się tak, ponieważ gdy strumień jest tymczasowy, ADL kończy się niepowodzeniem, a powyższy szablon nie jest możliwy. jakieś wskazówki?cout
instrukcje w jednącout
instrukcję, łącząc<<
operatory razem. Zobacz tutaj(Nie wolno mi jeszcze komentować). Sugerowałbym następujące ulepszenia już świetnej odpowiedzi Jamesa McNellisa:
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); }
z
constexpr
: pozwala mi użyć wartości elementu wyliczenia jako rozmiaru tablicy w czasie kompilacjistatic_assert
+is_enum
: aby 'zapewnić' w czasie kompilacji, że funkcja coś wykona. tylko z wyliczeniami, zgodnie z sugestiąSwoją drogą, zadaję sobie pytanie: dlaczego powinienem używać,
enum class
kiedy chciałbym przypisać wartości liczbowe moim członkom wyliczenia? Biorąc pod uwagę wysiłek związany z konwersją.Być może wtedy wróciłbym do zwykłego,
enum
jak zasugerowałem tutaj: Jak używać wyliczeń jako flag w C ++?Jeszcze inny (lepszy) smak bez static_assert, oparty na sugestii @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); }
źródło
T
dla któregostd::underlying_type<T>::type
istnieje, alestd::is_enum<T>::value
jest fałszywy? Jeśli nie, tostatic_assert
nie dodaje żadnej wartości.Enumeration
nie jest pełnym typem wyliczenia. W takim przypadku może być już za późno, ponieważ jest używany w zwracanym typie. Może moglibyśmy określićstd::enable_if<std::is_enum<Enumeration>::value, std::underlying_type<Enumeration>::type>
jako typ zwrotu? Oczywiście jest o wiele łatwiej (a komunikaty o błędach o wiele wyraźniejsze), jeśli masz kompilator z obsługą Concepts ...Pisać prościej,
enum class Color { Red = 1, Green = 11, Blue = 111 }; int value = static_cast<int>(Color::Blue); // 111
źródło
Pracowałem dla mnie w 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); }
źródło
Możesz zrobić coś takiego:
//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;
źródło