คลาส enum สามารถแปลงเป็นประเภทพื้นฐานได้หรือไม่


113

มีวิธีการแปลง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
FYI กฎนี้กำหนดไว้ใน[C++11: 7.2/9].
Lightness Races ใน Orbit

5
@Pubby เศร้าที่ไม่ได้กำหนดขอบเขต 'enum' ก่อให้เกิดมลพิษต่อขอบเขตภายนอกด้วยตัวนับทั้งหมด อนิจจาไม่มีโลกที่ดีที่สุด (ตั้งแต่ C ++ 14 อยู่ดี) ซึ่งทำรังขอบเขตอย่างสมบูรณ์ในขณะที่แปลงเป็นประเภทฐานโดยปริยาย (ซึ่งค่อนข้างไม่สอดคล้องกับวิธีที่ C ++ จัดการกับการสืบทอดคลาสอื่น ๆ เมื่อคุณส่งผ่านประเภทที่ได้รับมากขึ้นโดย ค่าหรือการอ้างอิงถึงฟังก์ชันที่ใช้ประเภทฐาน)
Dwayne Robinson

3
@DwayneRobinson ใช่มี. ติด enum ที่ไม่ได้กำหนดขอบเขตไว้ในโครงสร้างหรือ (ดีกว่า) เนมสเปซ ดังนั้นจึงมีการกำหนดขอบเขตและยังคงมีการแปลง int โดยปริยาย (แม้ว่าฉันจะต้องคิดสองครั้งว่าทำไมคุณต้องแปลงเป็น int และอาจพิจารณาว่ามีแนวทางที่ดีกว่านี้หรือไม่)
Pharap

คำตอบ:


178

ฉันคิดว่าคุณสามารถใช้std :: underlying_typeเพื่อทราบประเภทพื้นฐานจากนั้นใช้ cast:

#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: ขอบคุณ (BTW คุณก็มี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

นอกจากนี้โปรดทราบความจริงที่ว่าอัฒภาคควรอยู่หลังวงเล็บปีกกาปิดในคำจำกัดความของ enum ไม่ใช่ก่อนหน้านี้


0

ฉันพบว่าฟังก์ชันต่อไปนี้underlying_castมีประโยชน์เมื่อต้องต่ออนุกรมค่า 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

ตามที่คนอื่นชี้ว่าไม่มีการโยนโดยนัย แต่คุณสามารถใช้ Explicit static_castได้ ฉันใช้ฟังก์ชันตัวช่วยต่อไปนี้ในโค้ดของฉันเพื่อแปลงเป็นและจากประเภท enum และคลาสพื้นฐาน

    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.