2012-07-10 21 views
56

¿Cómo puedo generar el valor de enum class en C++ 11? En C++ 03 es la siguiente:¿Cómo puedo generar el valor de una clase enum en C++ 11

#include <iostream> 

using namespace std; 

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

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

en C++ 0x este código no compila

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

compilan en Ideone.com

+1

¿Por qué estás tratando de enviar enum? la clase enum se usa para no mezclar los valores enum con la representación int. – RiaD

Respuesta

75

A diferencia de una enumeración sin ámbito, una enumeración con ámbito es no implícitamente convertible a su valor entero. Es necesario que explícitamente convertirlo en un entero usando un reparto:

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

es posible que desee encapsular la lógica en una plantilla de función:

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

utilizado como:

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

¿Hay alguna razón por la que esto utiliza la sintaxis del tipo de retorno final? –

+0

@NicolBolas: copié 'as_integer' de una de mis bibliotecas de código abierto, CxxReflect (consulte [enumeration.hpp] (http://cxxreflect.codeplex.com/SourceControl/changeset/view/8ffbb562ad38#cxxreflect%2fcore%2fenumeration .hpp)). La biblioteca utiliza los tipos de retorno final consistentemente, en todas partes. Por consistencia. –

+6

Aunque esto tiene 2 años de retraso, en caso de que alguien más vea esta pregunta, puede usar el método de conversión de yeso anterior y simplemente llamar a "static_cast (valor)" para obtener el entero o "static_cast (intValue)" para obtener una enumeración valor. Solo tenga en cuenta que pasar de int a enum o enum a enum puede causar problemas y generalmente es en general un signo de error de diseño. –

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

Copié este ejemplo textualmente y lo compilé como 'g ++ -std = C++ 0x enum.cpp' pero obtengo un montón de errores de compilación -> http://pastebin.com/JAtLXan9. Tampoco pude obtener el ejemplo de @ james-mcnellis para compilar. – Dennis

+3

@Dennis [tipo_inicial] (http://www.cplusplus.com/reference/type_traits/underlying_type/) está solo en C++ 11 – Deqing

9

(No tengo permiso para comentar aún) Sugeriría las siguientes mejoras para la ya gran respuesta de 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); 
} 

con

  • constexpr: permitir que utilice un valor de miembro de la enumeración como en tiempo de compilación tamaño de la matriz
  • static_assert + is_enum: la de garantizar compilar -tiempo que la función hace algo con enumeraciones solamente, como se sugiere

Por cierto que estoy preguntando a mí mismo: ¿Por qué debería utilizar nunca enum class cuando quisiera asignar valores numéricos a los miembros de mi enumeración ?! Teniendo en cuenta el esfuerzo de conversión.

Tal vez me permitiría volver a enum ordinaria como he sugerido aquí: How to use enums as flags in C++?


Sin embargo, otro (mejor) el sabor de la misma sin static_assert, basado en una sugerencia de @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

¿Hay un tipo 'T' para el que' std :: underlying_type :: type 'existe, pero' std :: is_enum :: value' es falso? Si no, entonces 'static_assert' no agrega ningún valor. –

+0

No probé en todos los compiladores. Pero, @TobySpeight, probablemente tengas razón, msvc2013 parece escupir mensajes de error comprensibles, lo que sugiere una correspondencia 1-a-1 entre el tipo subyacente_existente y el tipo en sí mismo enum. Y static_assert ni siquiera se dispara. Pero: la referencia dice que el comportamiento de tipo_inserto no está definido si no se proporciona con un tipo de enumeración completo. Entonces el static_assert es solo una esperanza para obtener un mensaje máximo comprensible por si acaso. ¿Tal vez hay posibilidades de forzarlo a procesar antes/antes? – yau

+0

Ah sí, tienes razón en que no está definido si 'Enumeration' no es un tipo de enumeración completo. En ese caso, puede que ya sea demasiado tarde, ya que se usa en el tipo de devolución. Quizás podríamos especificar 'std :: enable_if :: value, std :: underlying_type :: type>' como el tipo de devolución? Por supuesto, es mucho más fácil (y los mensajes de error son mucho más claros) si tiene un compilador con soporte para Conceptos ... –

14

Es posible obtener su segundo ejemplo (es decir, el que usa una enumeración de ámbito) para trabajar con la misma sintaxis que las enumeraciones sin ámbito. Además, la solución es genérica y funcionará para todas las enumeraciones con ámbito, en comparación con el código de escritura para cada enum con ámbito (como se muestra en el answer proporcionado por @ForEveR).

La solución es escribir una función genérica operator<< que funcionará para cualquier enum de ámbito. La solución emplea SFINAE a través de std::enable_if y es la siguiente.

#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

Necesita 'typename' antes de' std :: underlying_type :: type'. – uckelman

+0

@uckelman Estás en lo cierto. Gracias por actualizar mi respuesta. –

+0

esto funcionó para mí bajo clang, pero en gcc 4.9.2, esta solución falla al encadenar << juntos, con el error 'error: no se puede enlazar 'std :: basic_ostream ' lvalue a 'std :: basic_ostream &&' '. esto parece ser porque cuando la transmisión es temporal, la ADL falla, y la plantilla anterior no es una posibilidad. ¿algun consejo? – ofloveandhate

Cuestiones relacionadas