È possibile convertire una classe enum nel tipo sottostante?


112

C'è un modo per convertire un enum classcampo nel tipo sottostante? Ho pensato che sarebbe stato automatico, ma a quanto pare no.

enum class my_fields : unsigned { field = 1 };

unsigned a = my_fields::field;

Tale incarico è stato rifiutato da GCC. error: cannot convert 'my_fields' to 'unsigned int' in assignment.


4
Se vuoi convertire nel tipo sottostante, usa enum.
Pubby

1
Cordiali saluti, questa regola è definita in [C++11: 7.2/9].
Gare di leggerezza in orbita il

5
@ Pubby Purtroppo 'enum' senza ambito inquina l'ambito esterno con tutti gli enumeranti. Purtroppo non esiste il meglio di entrambi i mondi (come di C ++ 14 comunque) che annida in modo pulito l'ambito mentre converte anche implicitamente nel tipo di base (che è piuttosto incoerente con il modo in cui C ++ gestisce l'ereditarietà di altre classi, quando si passa un tipo più derivato da valore o riferimento a una funzione che assume un tipo base).
Dwayne Robinson

2
@DwayneRobinson Sì, c'è. Incolla un'enumerazione senza ambito all'interno di una struttura o (più preferibilmente) in uno spazio dei nomi. Quindi è con ambito e ha ancora la conversione int implicita. (Anche se sarei sicuro di pensarci due volte sul motivo per cui è necessario convertire in un int e forse considerare se esiste un approccio migliore.)
Pharap

Risposte:


178

Penso che tu possa usare std :: sottostante_type per conoscere il tipo sottostante, e quindi usare cast:

#include <type_traits> //for std::underlying_type

typedef std::underlying_type<my_fields>::type utype;

utype a = static_cast<utype>(my_fields::field);

Con questo, non devi assumere il tipo sottostante, o non devi menzionarlo nella definizione di enum classsimili enum class my_fields : int { .... }o giù di lì.

Puoi persino scrivere una funzione di conversione generica che dovrebbe essere in grado di convertire qualsiasi enum class nel suo tipo integrale sottostante :

template<typename E>
constexpr auto to_integral(E e) -> typename std::underlying_type<E>::type 
{
   return static_cast<typename std::underlying_type<E>::type>(e);
}

quindi usalo:

auto value = to_integral(my_fields::field);

auto redValue = to_integral(Color::Red);//where Color is an enum class!

E poiché la funzione è dichiarata essere constexpr, puoi usarla dove è richiesta un'espressione costante:

int a[to_integral(my_fields::field)]; //declaring an array

std::array<int, to_integral(my_fields::field)> b; //better!

Ora che siamo nel futuro:template <typename T> auto to_integral(T e) { return static_cast<std::underlying_type_t<T>>(e); }
Ryan Haining

1
@ RyanHaining: Grazie. (BTW, ce constexprl' hai anche in futuro; in effetti uno molto più potente di quello che avevo nel 2013: P)
Nawaz

41

Non puoi convertirlo implicitamente , ma è possibile un cast esplicito:

enum class my_fields : unsigned { field = 1 };

// ...

unsigned x = my_fields::field; // ERROR!
unsigned x = static_cast<unsigned>(my_fields::field); // OK

Ricorda anche il fatto che il punto e virgola dovrebbe essere dopo la parentesi graffa chiusa nella definizione dell'enumerazione, non prima.


0

Trovo la seguente funzione underlying_castutile quando si devono serializzare correttamente i valori enum.

namespace util
{

namespace detail
{
    template <typename E>
    using UnderlyingType = typename std::underlying_type<E>::type;

    template <typename E>
    using EnumTypesOnly = typename std::enable_if<std::is_enum<E>::value, E>::type;

}   // namespace util.detail


template <typename E, typename = detail::EnumTypesOnly<E>>
constexpr detail::UnderlyingType<E> underlying_cast(E e) {
    return static_cast<detail::UnderlyingType<E>>(e);
}

}   // namespace util

enum SomeEnum : uint16_t { A, B };

void write(SomeEnum /*e*/) {
    std::cout << "SomeEnum!\n";
}

void write(uint16_t /*v*/) {
    std::cout << "uint16_t!\n";
}

int main(int argc, char* argv[]) {
    SomeEnum e = B;
    write(util::underlying_cast(e));
    return 0;
}

0

Come altri hanno sottolineato non esiste un cast implicito, ma puoi usare un cast esplicito static_cast. Uso le seguenti funzioni di supporto nel mio codice per convertire da e verso un tipo enum e la sua classe sottostante.

    template<typename EnumType>
    constexpr inline decltype(auto) getIntegralEnumValue(EnumType enumValue)
    {
        static_assert(std::is_enum<EnumType>::value,"Enum type required");
        using EnumValueType = std::underlying_type_t<EnumType>;
        return static_cast<EnumValueType>(enumValue);
    }

    template<typename EnumType,typename IntegralType>
    constexpr inline EnumType toEnum(IntegralType value)
    {
        static_assert(std::is_enum<EnumType>::value,"Enum type required");
        static_assert(std::is_integral<IntegralType>::value, "Integer required");
        return static_cast<EnumType>(value);
    }

    template<typename EnumType,typename UnaryFunction>
    constexpr inline void setIntegralEnumValue(EnumType& enumValue, UnaryFunction integralWritingFunction)
    {
        // Since using reinterpret_cast on reference to underlying enum type is UB must declare underlying type value and write to it and then cast it to enum type
        // See discussion on /programming/19476818/is-it-safe-to-reinterpret-cast-an-enum-class-variable-to-a-reference-of-the-unde

        static_assert(std::is_enum<EnumType>::value,"Enum type required");

        auto enumIntegralValue = getIntegralEnumValue(enumValue);
        integralWritingFunction(enumIntegralValue);
        enumValue = toEnum<EnumType>(enumIntegralValue);
    }

Codice di utilizzo

enum class MyEnum {
   first = 1,
   second
};

MyEnum myEnum = MyEnum::first;
std::cout << getIntegralEnumValue(myEnum); // prints 1

MyEnum convertedEnum = toEnum(1);

setIntegralEnumValue(convertedEnum,[](auto& integralValue) { ++integralValue; });
std::cout << getIntegralEnumValue(convertedEnum); // prints 2
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.