Une classe enum peut-elle être convertie en type sous-jacent?

112

Existe-t-il un moyen de convertir un enum classchamp en type sous-jacent? Je pensais que ce serait automatique, mais apparemment pas.

enum class my_fields : unsigned { field = 1 };

unsigned a = my_fields::field;

Cette affectation est rejetée par GCC. error: cannot convert 'my_fields' to 'unsigned int' in assignment.

edA-qa mort-ora-y
la source
4
Si vous souhaitez convertir en type sous-jacent, utilisez enum.
Pubby
1
Pour info, cette règle est définie dans [C++11: 7.2/9].
Courses de légèreté en orbite
5
@Pubby Malheureusement, 'enum' sans portée pollue la portée externe avec tous les énumérants. Hélas, il n'y a pas le meilleur des deux mondes (à partir de C ++ 14 de toute façon) qui imbrique proprement la portée tout en effectuant une conversion implicite vers le type de base (ce qui est plutôt incohérent avec la façon dont C ++ gère l'héritage des autres classes, lorsque vous passez un type plus dérivé par valeur ou référence à une fonction prenant un type de base).
Dwayne Robinson
2
@DwayneRobinson Oui, il y en a. Collez une énumération sans portée dans une structure ou (plus préférablement) un espace de noms. Ainsi, il a une portée et a toujours la conversion implicite int. (Bien que je ne manquerais pas de réfléchir à deux fois à la raison pour laquelle vous devez convertir en un int et peut-être envisager s'il existe une meilleure approche.)
Pharap

Réponses:

178

Je pense que vous pouvez utiliser std :: sous-jacent_type pour connaître le type sous-jacent, puis utiliser cast:

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

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

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

Avec cela, vous n'avez pas à assumer le type sous-jacent, ou vous n'avez pas à le mentionner dans la définition de quelque chose de enum classsimilaire enum class my_fields : int { .... }.

Vous pouvez même écrire une fonction de conversion générique qui devrait être capable de convertir n'importe quelle fonction enum class en son type intégral sous-jacent :

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

puis utilisez-le:

auto value = to_integral(my_fields::field);

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

Et puisque la fonction est déclarée être constexpr, vous pouvez l'utiliser là où une expression constante est requise:

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

std::array<int, to_integral(my_fields::field)> b; //better!
Nawaz
la source
Maintenant que nous sommes dans le futur:template <typename T> auto to_integral(T e) { return static_cast<std::underlying_type_t<T>>(e); }
Ryan Haining
1
@RyanHaining: Merci. (BTW, vous en avez constexpraussi dans le futur; en fait beaucoup plus puissant que ce que j'avais en 2013: P)
Nawaz
41

Vous ne pouvez pas le convertir implicitement , mais un cast explicite est possible:

enum class my_fields : unsigned { field = 1 };

// ...

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

Gardez également à l'esprit que le point-virgule doit être après l'accolade fermée dans la définition de votre énumération, pas avant.

Andy Prowl
la source
0

Je trouve la fonction suivante underlying_castutile lorsque je dois sérialiser correctement les valeurs d'énumération.

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
la source
0

Comme d'autres l'ont souligné, il n'y a pas de conversion implicite, mais vous pouvez utiliser un fichier explicite static_cast. J'utilise les fonctions d'assistance suivantes dans mon code pour convertir vers et depuis un type enum et sa classe sous-jacente.

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

Code d'utilisation

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
la source