Một lớp enum có thể được chuyển đổi thành kiểu cơ bản không?


112

Có cách nào để chuyển đổi một enum classtrường thành kiểu cơ bản không? Tôi nghĩ rằng điều này sẽ tự động, nhưng dường như không.

enum class my_fields : unsigned { field = 1 };

unsigned a = my_fields::field;

Nhiệm vụ đó đang bị GCC từ chối. error: cannot convert 'my_fields' to 'unsigned int' in assignment.


4
Nếu bạn muốn chuyển đổi sang kiểu cơ bản thì hãy sử dụng enum.
Pubby

1
FYI, quy tắc này được định nghĩa trong [C++11: 7.2/9].
Các cuộc đua ánh sáng trong quỹ đạo

5
@Pubby Đáng buồn thay, 'enum' không được dán nhãn gây ô nhiễm phạm vi bên ngoài với tất cả các điều tra viên. Than ôi là không có điều tốt nhất trong cả hai thế giới (dù sao cũng như C ++ 14) mà tổ chức phạm vi một cách rõ ràng trong khi cũng chuyển đổi ngầm sang kiểu cơ sở (điều này khá mâu thuẫn với cách C ++ xử lý kế thừa lớp khác, khi bạn chuyển một kiểu dẫn xuất hơn bằng cách giá trị hoặc tham chiếu đến một hàm nhận kiểu cơ sở).
Dwayne Robinson

2
@DwayneRobinson Vâng, có. Dán một enum chưa được mở trong một cấu trúc hoặc (tốt hơn là) một không gian tên. Do đó, nó có phạm vi và vẫn có chuyển đổi int ngầm định. (Mặc dù tôi muốn hãy chắc chắn để suy nghĩ hai lần về việc tại sao bạn cần phải chuyển đổi sang một int và có lẽ xem xét nếu có một cách tiếp cận tốt hơn.)
Pharap

Câu trả lời:


178

Tôi nghĩ bạn có thể sử dụng std :: underlying_type để biết loại cơ bản và sau đó sử dụng ép kiểu:

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

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

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

Với điều này, bạn không phải giả định loại cơ bản hoặc bạn không cần phải đề cập đến nó trong định nghĩa của loại enum classtương tự enum class my_fields : int { .... }.

Bạn thậm chí có thể viết một hàm chuyển đổi chung có thể chuyển đổi bất kỳ enum class thành kiểu tích phân cơ bản của nó :

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

sau đó sử dụng nó:

auto value = to_integral(my_fields::field);

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

Và vì hàm được khai báo là constexpr, bạn có thể sử dụng nó khi cần có biểu thức hằng số:

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

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

Bây giờ chúng ta đang ở trong tương lai:template <typename T> auto to_integral(T e) { return static_cast<std::underlying_type_t<T>>(e); }
Ryan Haining

1
@RyanHain: Cảm ơn. (BTW, bạn có constexprcũng như trong tương lai, trong thực tế mạnh hơn rất nhiều so với những gì người ta tôi đã có trong năm 2013: P)
Nawaz

41

Bạn không thể chuyển đổi nó một cách hoàn toàn , nhưng có thể thực hiện một phép truyền rõ ràng:

enum class my_fields : unsigned { field = 1 };

// ...

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

Cũng xin lưu ý thực tế rằng dấu chấm phẩy phải nằm sau dấu ngoặc nhọn đóng trong định nghĩa enum của bạn, không phải trước.


0

Tôi thấy chức năng sau đây underlying_casthữu ích khi phải tuần tự hóa các giá trị enum một cách chính xác.

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

Như những người khác đã chỉ ra rằng không có diễn viên ngầm, nhưng bạn có thể sử dụng một cách rõ ràng static_cast. Tôi sử dụng các hàm trợ giúp sau trong mã của mình để chuyển đổi sang và từ một kiểu enum và lớp cơ bản của nó.

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

Mã sử ​​dụng

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.