web-dev-qa-db-fra.com

Comment puis-je afficher la valeur d'une classe enum en C++ 11

Comment puis-je afficher la valeur d'un enum class en C++ 11? En C++ 03, c'est comme ça:

#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 ce code ne compile pas

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

compilé à Ideone.com

65
Adi

Contrairement à une énumération non délimitée, une énumération délimitée n'est pas implicitement convertible en valeur entière. Vous devez explicitement le convertir en entier à l'aide d'un transtypage:

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

Vous voudrez peut-être encapsuler la logique dans un modèle de fonction:

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

utilisé comme:

std::cout << as_integer(a) << std::endl;
86
James McNellis
#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;
}
30
ForEveR

Il est possible d’obtenir votre deuxième exemple (c’est-à-dire celui utilisant une énumération étendue) pour qu’il utilise la même syntaxe que l’énumération non délimitée. De plus, la solution est générique et fonctionnera pour toutes les énumérations de portée, par opposition à l'écriture de code pour chaque énumération de portée (comme indiqué dans le answer fourni par @ForEveR ).

La solution consiste à écrire une fonction operator<< générique qui fonctionnera pour toute énumération de portée. La solution utilise SFINAE via std::enable_if et est la suivante.

#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;
}
15
James Adkison

(Je ne suis pas autorisé à commenter pour le moment.) Je suggérerais les améliorations suivantes à la réponse déjà excellente 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);
}

avec

  • constexpr: me permettant d'utiliser une valeur de membre enum comme taille de tableau au moment de la compilation
  • static_assert + is_enum: pour 's'assurer' à la compilation que la fonction fait qc. avec énumérations seulement, comme suggéré

En passant, je me pose la question suivante: pourquoi devrais-je jamais utiliser enum class lorsque je voudrais attribuer des valeurs numériques aux membres de mon enum?! Considérant l'effort de conversion.

Peut-être que je reviendrais alors à la variable ordinaire enum, comme je l’ai suggéré ici: Comment utiliser les énumérations comme drapeaux en C++?


Encore une autre (meilleure) version de celle-ci sans static_assert, basée sur une suggestion 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);
}
9
yau

Pour écrire plus simplement,

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

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

La suite a fonctionné pour moi en 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);
}
0
NutCracker