Bir enum sınıfı temel alınan türe dönüştürülebilir mi?


112

Bir enum classalanı temeldeki türe dönüştürmenin bir yolu var mı ? Bunun otomatik olacağını düşünmüştüm, ama görünüşe göre değil.

enum class my_fields : unsigned { field = 1 };

unsigned a = my_fields::field;

Bu atama GCC tarafından reddediliyor. error: cannot convert 'my_fields' to 'unsigned int' in assignment.


4
Temel türe dönüştürmek istiyorsanız, o zaman kullanın enum.
Pubby

1
Bilginize, bu kural içinde tanımlanmıştır [C++11: 7.2/9].
Orbit'te Hafiflik Yarışları

5
@Pubby Ne yazık ki kapsamı kaldırılmamış 'enum' dış kapsamı tüm numaralandırıcılarla kirletiyor. Ne yazık ki, kapsamı temiz bir şekilde iç içe geçiren ve aynı zamanda örtük olarak temel türe dönüştüren (bu, daha türetilmiş bir türü geçtiğinizde C ++ 'nın diğer sınıf kalıtımını nasıl işlediğiyle oldukça tutarsızdır) her iki dünyanın en iyisi yoktur (yine de C ++ 14 itibariyle). temel tür alan bir işleve değer veya başvuru).
DwayneRobinson

2
@DwayneRobinson Evet var. Kapsamı kaldırılmış bir numaralandırmayı bir yapının veya (daha tercihen) bir ad alanının içine yapıştırın. Bu nedenle, kapsamı belirlenir ve örtük int dönüşüme sahiptir. (Yine de neden bir int
türüne

Yanıtlar:


178

Sanırım temeldeki türü öğrenmek için std :: underlying_type ve ardından cast'ı kullanabilirsiniz:

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

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

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

Bununla , temel türü üstlenmek zorunda değilsiniz veya enum classbenzerinin tanımında belirtmek zorunda değilsiniz enum class my_fields : int { .... }.

Herhangi birini temeldeki integral türüne dönüştürebilecek genel bir dönüştürme işlevi bile yazabilirsiniz : enum class

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

sonra kullan:

auto value = to_integral(my_fields::field);

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

Ve fonksiyon olarak bildirildiğinden constexpr, onu sabit ifadenin gerekli olduğu yerlerde kullanabilirsiniz:

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

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

Şimdi geleceğin içindeyiz:template <typename T> auto to_integral(T e) { return static_cast<std::underlying_type_t<T>>(e); }
Ryan Haining

1
@RyanHaining: Teşekkürler. (BTW, constexprgelecekte de var; aslında benim 2013'tekinden çok daha güçlü: P)
Nawaz

41

Örtük olarak dönüştüremezsiniz , ancak açık bir atama mümkündür:

enum class my_fields : unsigned { field = 1 };

// ...

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

Ayrıca noktalı virgülün , numaralandırmanızın tanımındaki kapalı küme ayracından sonra olması gerektiğine dikkat edin, daha önce değil.


0

underlying_castEnum değerlerini doğru şekilde serileştirmek zorunda kaldığımda aşağıdaki işlevi yararlı buluyorum .

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

Diğerlerinin de belirttiği gibi, örtük bir atama yoktur, ancak açık bir şekilde kullanabilirsiniz static_cast. Kodumda bir enum türüne ve onun temelindeki sınıfına dönüştürmek için aşağıdaki yardımcı işlevleri kullanıyorum.

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

Kullanım kodu

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.