2012-07-10 7 views
56

Wie kann ich den Wert einer enum class in C++ 11 ausgeben? In C++ 03 ist es wie folgt aus:Wie kann ich den Wert einer Enum-Klasse in C++ ausgeben 11

#include <iostream> 

using namespace std; 

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

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

in C++ 0x dieser Code nicht

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

bei Ideone.com

+1

Warum möchten Sie enum ausgeben? enum-Klasse wird verwendet, um enum-Werte nicht mit int-Darstellung zu verwechseln – RiaD

Antwort

75

Im Gegensatz zu einer unscoped Aufzählung kompiliert nicht kompiliert, ein scoped Aufzählung nicht implizit umwandelbar zu seinem ganzzahligen Wert. Sie müssen explizit es mit einer Umwandlung in eine ganze Zahl konvertieren:

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

Möglicherweise möchten Sie die Logik in eine Funktionsvorlage kapseln:

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

als gebraucht:

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

Gibt es einen Grund dafür, dass dies nachgestellte Rückgabesyntax verwendet? –

+0

@NicolBolas: Ich habe 'as_integer' aus einer meiner Open-Source-Bibliotheken kopiert, CxxReflect (siehe [enumeration.hpp] (http://cxxreflect.codeplex.com/SourceControl/changeset/view/8ffbb562ad38#cxxreflect%2fcore%2fenumeration) .hpp)). Die Bibliothek verwendet fortlaufende Rückgabetypen konsistent und überall. Aus Konsistenzgründen. –

+6

Obwohl dies 2 Jahre zu spät ist, können Sie für den Fall, dass jemand anderes diese Frage sieht, einfach die obige Cast-Technik verwenden und einfach "static_cast (value)" aufrufen, um die Ganzzahl oder "static_cast (intValue)" zu erhalten Wert. Bedenken Sie, dass der Übergang von int zu enum oder enum zu enum zu Problemen führen kann und generell ein Zeichen für einen Designfehler ist. –

26
#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; 
} 
+0

Ich kopierte dieses Beispiel wörtlich und kompilierte es als 'g ++ -std = C++ 0x enum.cpp', aber ich bekomme eine Reihe von Compiler-Fehlern -> http://pastebin.com/JAtLXan9. Ich konnte das Beispiel von @ james-mcellis auch nicht kompilieren. – Dennis

+3

@Dennis [zugrunde liegenden_Typ] (http://www.cplusplus.com/reference/type_traits/underlying_type/) ist nur in C++ 11 – Deqing

9

(Ich darf noch nicht kommentieren.) Ich würde die folgenden Verbesserungen vorschlagen die bereits große Antwort von 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); 
} 

mit

  • constexpr: Erlaubt mir einen ENUM Mitglied Wert als Kompilierung-Array-Größe zu verwenden
  • static_assert + is_enum: zu 'sichern' kompilieren -Zeit, dass die Funktion etw. nur mit Aufzählungen, wie
  • vorgeschlagen

Durch die Art, wie ich mich frage: Warum sollte ich jemals enum class verwenden, wenn ich möchte Zahlenwerte an meine Enum-Mitglieder zuweisen ?! Bedenkt den Umstellungsaufwand.

Vielleicht würde ich dann auf gewöhnliche enum gehen zurück, als ich hier vorgeschlagen: How to use enums as flags in C++?


Noch einen anderen (besser) Geschmack davon ohne static_assert, basierend auf einem Vorschlag von @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); 
} 
+0

Gibt es einen Typ "T" für die "Std :: Underlying_Type :: Typ 'existiert, aber' std :: is_enum :: value' ist falsch? Wenn nicht, fügt der 'static_assert' keinen Wert hinzu. –

+0

Ich habe nicht an allen Compilern getestet. Aber, @ TobySpeight haben Sie wahrscheinlich Recht, msvc2013 scheint verständliche Fehlermeldungen auszuspucken, was eine 1-zu-1-Entsprechung zwischen dem zugrunde liegenden_Typ_t und dem Typ selbst vermuten lässt. Und static_assert wird nicht einmal ausgelöst. Aber: Die Referenz besagt, dass das Verhalten von "unterliegender_typ" nicht definiert ist, wenn es nicht mit einem vollständigen Aufzählungstyp versehen ist. So ist die static_assert nur eine Hoffnung, eine möglichst verständliche Nachricht zu erhalten. Vielleicht gibt es Möglichkeiten, die Verarbeitung früher/frühestens zu erzwingen? – yau

+0

Ach ja, du hast Recht, dass es undefiniert ist, wenn 'Enumeration' kein vollständiger Enum-Typ ist. In diesem Fall ist es möglicherweise bereits zu spät, da es im Rückgabetyp verwendet wird. Vielleicht könnten wir 'std :: enable_if :: value, std :: unterlegener_ type :: type>' als Rückgabetyp angeben? Natürlich, es ist so viel einfacher (und die Fehlermeldungen so viel klarer), wenn Sie einen Compiler mit Unterstützung für Konzepte haben ... –

14

Es ist möglich, Ihr zweites Beispiel (dh das, das eine bereichsspezifische Enumeration verwendet) zu verwenden, um die gleiche Syntax wie nicht aufgezeichnete Enums zu verwenden. Darüber hinaus ist die Lösung generisch und wird für alle Bereichsaufzählungen funktionieren, im Gegensatz zum Schreiben von Code für jede Bereichsaufzählung (wie in answer von @ForEveR gezeigt).

Die Lösung besteht darin, eine generische operator<<-Funktion zu schreiben, die für beliebige Bereichsaufzählungen funktioniert. Die Lösung verwendet SFINAE über std::enable_if und ist wie folgt.

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

Sie benötigen einen 'type' vor' std :: unterliegenden_type :: type'. – uckelman

+0

@uckelman Du hast absolut recht. Danke, dass du meine Antwort aktualisiert hast. –

+0

dies funktionierte für mich unter clang, aber unter gcc 4.9.2, schlägt diese Lösung bei der Verkettung << zusammen, mit dem Fehler 'Fehler: kann nicht binden 'std :: basic_ostream ' lvalue zu 'std :: basic_ostream &&' '. Dies scheint zu sein, da der ADL fehlschlägt, wenn der Stream temporär ist, und die obige Vorlage ist keine Möglichkeit. Irgendwelche Tipps? – ofloveandhate

Verwandte Themen