¿Se puede convertir una clase enum al tipo subyacente?

112

¿Hay alguna forma de convertir un enum classcampo al tipo subyacente? Pensé que esto sería automático, pero aparentemente no.

enum class my_fields : unsigned { field = 1 };

unsigned a = my_fields::field;

GCC está rechazando esa asignación. error: cannot convert 'my_fields' to 'unsigned int' in assignment.

edA-qa mort-ora-y
fuente
4
Si desea convertir a tipo subyacente, utilice enum.
Pubby
1
FYI, esta regla se define en [C++11: 7.2/9].
Lightness Races in Orbit
5
@Pubby Lamentablemente 'enum' sin ámbito contamina el ámbito externo con todos los enumeradores. Por desgracia, no hay lo mejor de ambos mundos (a partir de C ++ 14 de todos modos) que anida limpiamente el alcance al mismo tiempo que se convierte implícitamente al tipo base (que es bastante inconsistente con la forma en que C ++ maneja la herencia de otra clase, cuando se pasa un tipo más derivado por valor o referencia a una función que toma un tipo base).
Dwayne Robinson
2
@DwayneRobinson Sí, lo hay. Inserte una enumeración sin ámbito dentro de una estructura o (más preferiblemente) un espacio de nombres. Por lo tanto, tiene un alcance y todavía tiene la conversión int implícita. (Aunque me aseguraría de pensar dos veces por qué necesita convertir a un int y quizás considerar si hay un enfoque mejor).
Pharap

Respuestas:

178

Creo que puede usar std :: subyacente_type para conocer el tipo subyacente y luego usar 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 esto, no tiene que asumir el tipo subyacente, o no tiene que mencionarlo en la definición de enum classsimilar enum class my_fields : int { .... }o algo así.

Incluso puede escribir una función de conversión genérica que debería poder convertir cualquiera enum class a su tipo integral subyacente :

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

luego úsalo:

auto value = to_integral(my_fields::field);

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

Y dado que se declara que la función es constexpr, puede usarla donde se requiera una expresión constante:

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

std::array<int, to_integral(my_fields::field)> b; //better!
Nawaz
fuente
Ahora que estamos en el futuro:template <typename T> auto to_integral(T e) { return static_cast<std::underlying_type_t<T>>(e); }
Ryan Haining
1
@RyanHaining: Gracias. (Por cierto, también lo tienes constexpren el futuro; de hecho, uno mucho más poderoso que el que tenía en 2013: P)
Nawaz
41

No puede convertirlo implícitamente , pero es posible una conversión explícita:

enum class my_fields : unsigned { field = 1 };

// ...

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

También tenga en cuenta el hecho de que el punto y coma debe estar después de la llave cerrada en la definición de su enumeración, no antes.

Andy Prowl
fuente
0

Encuentro la siguiente función underlying_castútil cuando tengo que serializar valores de enumeración correctamente.

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;
}
James
fuente
0

Como otros han señalado, no hay una conversión implícita, pero puede usar una explícita static_cast. Utilizo las siguientes funciones auxiliares en mi código para convertir hacia y desde un tipo de enumeración y su clase subyacente.

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

Código de uso

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
GameSalutes
fuente