列挙型クラスを基になる型に変換できますか?


112

enum classフィールドを基になる型に変換する方法はありますか?これは自動だと思ったが、どうやらそうではなかった。

enum class my_fields : unsigned { field = 1 };

unsigned a = my_fields::field;

その割り当てはGCCによって拒否されています。error: cannot convert 'my_fields' to 'unsigned int' in assignment


4
基になる型に変換する場合は、を使用しますenum
Pubby

1
参考までに、このルールはで定義されてい[C++11: 7.2/9]ます。
軌道上での明度レース

5
@Pubby残念なことに、スコープのない「列挙型」は、すべての列挙子で外側のスコープを汚染します。悲しいかな、両方の世界のベストはありません(とにかくC ++ 14現在)。スコープを明確に入れ子にしながら、基本型に暗黙的に変換します(これは、C ++が他のクラスの継承を処理する方法と矛盾します。値または基本型を取る関数への参照)。
ドウェインロビンソン、

2
@DwayneRobinsonはい、あります。スコープ外の列挙型を構造体または(より好ましくは)名前空間内に固定します。したがって、スコープが設定され、暗黙のint変換がまだあります。(私は必ずintに変換する必要がある理由について2度考えて、より良いアプローチがあるかどうかおそらく考えます。)
Pharap

回答:


178

std :: underlying_typeを使用して基になる型を確認し、キャストを使用できると思います。

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

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

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

これにより、基礎となる型を仮定する必要はありません。また、enum class同類enum class my_fields : int { .... }などの定義でそれを言及する必要もありません。

あなたも書くことができ、一般的な変換することができるはずコンバート機能いずれかを 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);
}

それを使用します:

auto value = to_integral(my_fields::field);

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

また、関数はとして宣言されているconstexprため、定数式が必要な場合に使用できます。

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

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

今、私たちは未来にいます:template <typename T> auto to_integral(T e) { return static_cast<std::underlying_type_t<T>>(e); }
Ryan Haining

1
@RyanHaining:ありがとう。(ところで、あなたもconstexpr将来的に持っています;実際、私が2013年に持っていたものよりもはるかに強力なものです:P)
Nawaz

41

暗黙的に変換することはできませんが、明示的なキャストが可能です。

enum class my_fields : unsigned { field = 1 };

// ...

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

また、セミコロンは、列挙型の定義では閉じ中括弧の後ろではなく、前に置く必要があることにも注意してください。


0

underlying_cast列挙値を正しくシリアル化する必要がある場合、次の関数が便利です。

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

他の人が指摘したように、暗黙のキャストはありませんが、明示的なキャストを使用できますstatic_cast。コードで次のヘルパー関数を使用して、列挙型とその基になるクラスとの間で変換を行います。

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

使用法コード

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.