Come posso visualizzare il valore di una classe enum in C ++ 11


96

Come posso visualizzare il valore di un enum classin C ++ 11? In C ++ 03 è così:

#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 questo codice non viene compilato

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

compilato su Ideone.com


1
Perché stai cercando di produrre enum? La classe enum viene utilizzata per non confondere i valori enum con la rappresentazione int
RiaD

Risposte:


122

A differenza di un'enumerazione senza ambito, un'enumerazione con ambito non è convertibile in modo implicito nel relativo valore intero. Devi convertirlo in modo esplicito in un numero intero usando un cast:

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

Potresti voler incapsulare la logica in un modello di funzione:

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

usato come:

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

3
C'è un motivo per cui utilizza la sintassi del tipo di ritorno finale?
Nicol Bolas

3
@NicolBolas: ho copiato as_integerda una delle mie librerie open source, CxxReflect (vedi enumeration.hpp ). La libreria utilizza i tipi di ritorno finali in modo coerente, ovunque. Per coerenza.
James McNellis

11
Anche se questo è in ritardo di 2 anni, nel caso in cui qualcun altro veda questa domanda puoi semplicemente usare il metodo della tecnica cast sopra e chiamare semplicemente "static_cast <int> (value)" per ottenere il numero intero o "static_cast <A> (intValue)" ottenere un valore enum. Tieni presente che passare da int a enum o da enum a enum può causare problemi e generalmente è un segno di un bug di progettazione.
Benjamin Danger Johnson

4
Anche int (value) e A (intValue) funzionano, senza le brutte parentesi angolari.
Grault

4
as_integerpuò essere definito in constexprmodo tale da poter essere utilizzato in contesti in cui è necessaria un'espressione costante.
Nawaz

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

Ho copiato questo esempio alla lettera e l'ho compilato come g++ -std=c++0x enum.cppma sto ricevendo un sacco di errori del compilatore -> pastebin.com/JAtLXan9 . Inoltre non sono riuscito a ottenere l'esempio da @ james-mcnellis da compilare.
Dennis

4
@Dennis sottostante_type è solo in C ++ 11
Deqing

23

È possibile far funzionare il secondo esempio (ovvero quello che utilizza un'enumerazione con ambito) utilizzando la stessa sintassi delle enumerazioni senza ambito. Inoltre, la soluzione è generica e funzionerà per tutte le enumerazioni con ambito, rispetto alla scrittura di codice per ciascuna enumerazione con ambito (come mostrato nella risposta fornita da @ForEveR ).

La soluzione è scrivere una operator<<funzione generica che funzioni per qualsiasi enumerazione con ambito. La soluzione impiega SFINAE via std::enable_ifed è la seguente.

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

Hai bisogno di una typenameprima std::underlying_type<T>::type.
uckelman

@uckelman Hai assolutamente ragione. Grazie per aver aggiornato la mia risposta.
James Adkison

questo ha funzionato per me sotto clang, ma sotto gcc 4.9.2, questa soluzione non riesce quando si concatena << insieme, con l'errore error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’. ciò sembra essere dovuto al fatto che quando il flusso è temporaneo, l'ADL non riesce e il modello precedente non è possibile. qualche consiglio?
ofloveandhate

@ofloveandhate Potresti fornire un collegamento a un esempio che produce il problema? Ho testato il codice sopra in gcc 4.9.2 senza problemi e solo una leggera modifica, ho convertito le 3 coutistruzioni in una singola coutistruzione concatenando gli <<operatori insieme. Vedi qui
James Adkison

permettimi di rivedere la mia dichiarazione. Stavo cercando di stampare una classe enum contenuta all'interno di una classe, dall'esterno di quella classe. il codice sopra funziona effettivamente per le classi enum non contenute all'interno di una classe stessa.
ofloveandhate

10

(Non sono ancora autorizzato a commentare.) Suggerirei i seguenti miglioramenti alla già ottima risposta di 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: permettendomi di usare un valore membro enum come dimensione dell'array in fase di compilazione
  • static_assert+ is_enum: per 'garantire' in fase di compilazione che la funzione esegua qc. solo con enumerazioni, come suggerito

A proposito, mi chiedo: perché dovrei mai usare enum classquando vorrei assegnare valori numerici ai miei membri enum ?! Considerando lo sforzo di conversione.

Forse tornerei all'ordinario enumcome ho suggerito qui: come utilizzare le enumerazioni come flag in C ++?


Ancora un altro (migliore) sapore senza static_assert, basato su un suggerimento di @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);
}

Esiste un tipo Tper il quale std::underlying_type<T>::typeesiste, ma std::is_enum<T>::valueè falso? In caso contrario, non static_assertaggiunge alcun valore.
Toby Speight

1
Non ho provato su tutti i compilatori. Ma, @TobySpeight probabilmente hai ragione, msvc2013 sembra sputare messaggi di errore comprensibili, suggerendo una corrispondenza 1 a 1 tra sottostante_type_t esistente e il tipo stesso che è enum. E static_assert non viene nemmeno licenziato. Ma: il riferimento dice che il comportamento di sottostante_type è indefinito se non viene fornito con un tipo enum completo. Quindi static_assert è solo una speranza di ottenere un messaggio comprensibile al massimo nel caso. Forse ci sono possibilità per forzare l'elaborazione prima / prima?
yau

Ah sì, hai ragione che non è definito se Enumerationnon è un tipo enum completo. In tal caso, potrebbe essere già troppo tardi, poiché viene utilizzato nel tipo restituito. Forse potremmo specificare std::enable_if<std::is_enum<Enumeration>::value, std::underlying_type<Enumeration>::type>come tipo di ritorno? Ovviamente, è molto più facile (ei messaggi di errore molto più chiari) se hai un compilatore con supporto per Concepts ...
Toby Speight

7

Per scrivere più semplice,

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

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

Questo non funzionerà quando all'enumerazione viene assegnato esplicitamente un tipo sottostante
James

3

Di seguito ha funzionato per me in 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

Potresti fare qualcosa del genere:

//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
La domanda posta su una classe enum.
Ant
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.