是否可以确定枚举是否为强类型?

时间:2018-08-17 19:19:43

标签: c++ c++11 enums typetraits

C ++ 11为处理枚举引入了两个不同的补充:一个使它们成为作用域的选项和一个使它们成为类型的选项。所以现在我们有四种不同的枚举子类型:

enum Old {};
enum Typed : int8_t {};
enum class Scoped {};
enum class TypedScoped : int8_t {};

This question询问如何确定枚举是否具有范围。我想知道如何确定是否键入枚举。


其他信息

我使用Qt框架,该框架提供了QDataStream类,以可移植的跨平台方式对数据进行序列化/反序列化。 显然,为了使结果数据流具有可移植性,您必须 以固定长度形式存储所有整数。这也包括枚举。 回想过去,我制作了几个辅助宏,通过将枚举转换为具有固定(用户指定)长度的整数来定义枚举的序列化/反序列化:

#define SC_DECLARE_DATASTREAM_WRITE_OPERATOR(_TYPE) \
    QDataStream &operator<<(QDataStream &stream, _TYPE v);

#define SC_DECLARE_DATASTREAM_READ_OPERATOR(_TYPE) \
    QDataStream &operator>>(QDataStream &stream, _TYPE &v);

#define SC_DECLARE_DATASTREAM_OPERATORS(_TYPE) \
    SC_DECLARE_DATASTREAM_WRITE_OPERATOR(_TYPE) \
    SC_DECLARE_DATASTREAM_READ_OPERATOR(_TYPE)

#define SC_DEFINE_DATASTREAM_ENUM_WRITE_OPERATOR(_TYPE, _LEN) \
    QDataStream &operator<<(QDataStream &stream, _TYPE v) \
{ \
    qint ## _LEN t = v; \
    static_assert(sizeof(t) >= sizeof(v), "Increase length"); \
    stream << t; \
    return stream; \
    }

#define SC_DEFINE_DATASTREAM_ENUM_READ_OPERATOR(_TYPE, _LEN) \
    QDataStream &operator>>(QDataStream &stream, _TYPE &v) \
{ \
    qint ## _LEN t {0}; \
    static_assert(sizeof(t) >= sizeof(v), "Increase length"); \
    stream >> t; \
    if(stream.status() == QDataStream::Ok) \
    v = static_cast<_TYPE>(t); \
    return stream; \
    }

#define SC_DEFINE_DATASTREAM_ENUM_OPERATORS(_TYPE, _LEN) \
    SC_DEFINE_DATASTREAM_ENUM_WRITE_OPERATOR(_TYPE, _LEN) \
    SC_DEFINE_DATASTREAM_ENUM_READ_OPERATOR(_TYPE, _LEN)

现在C ++ 11允许指定基础枚举类型,我可以简化上述宏:

#define SC_DEFINE_DATASTREAM_TYPED_ENUM_WRITE_OPERATOR(_TYPE) \
    QDataStream &operator<<(QDataStream &stream, _TYPE v) \
{ \
    const std::underlying_type<_TYPE>::type t {static_cast<std::underlying_type<_TYPE>::type>(v)}; \
    stream << t; \
    return stream; \
    }

#define SC_DEFINE_DATASTREAM_TYPED_ENUM_READ_OPERATOR(_TYPE) \
    QDataStream &operator>>(QDataStream &stream, _TYPE &v) \
{ \
    std::underlying_type<_TYPE>::type t {0}; \
    stream >> t; \
    if(stream.status() == QDataStream::Ok) \
    v = static_cast<_TYPE>(t); \
    return stream; \
    }

但是,如果用户不小心将新的(*_TYPED_*)宏用于未指定其基础类型的枚举,则会破坏对可移植性的保证,因为在不同平台上编译相同的代码可能会产生不同的结果。底层类型,并因此在序列化/反序列化代码中使用不同的整数长度。 我需要在代码中添加一个static_assert,如果枚举在声明时未进行强类型键入,则会破坏编译过程。

2 个答案:

答案 0 :(得分:1)

std::underlying_type可用于将编译限制为一组fixed width integer types(例如std::is_same):

#include <type_traits>
#include <cstdint>

template <typename T>
    constexpr bool is_fixed =
        std::is_same<T, std::int8_t>::value ||
        std::is_same<T, std::int16_t>::value
        // etc..
    ;

enum class E1 : std::int8_t {};
    static_assert( is_fixed<std::underlying_type_t<E1>>, "fixed");

enum class E2 {};
    static_assert(!is_fixed<std::underlying_type_t<E2>>, "not fixed");

变量模板确实是C ++ 14以来的版本,但是在C ++ 11中,可以使用constexpr函数或struct / class实现相同的模板:

template <typename T>
    constexpr bool is_fixed_f() {
        return  std::is_same<T, std::int8_t>::value ||
                std::is_same<T, std::int16_t>::value
                // etc..
        ;
    }

template <typename T>
    struct is_fixed_s {
        static constexpr bool value =
            std::is_same<T, std::int8_t>::value ||
            std::is_same<T, std::int16_t>::value
            // etc..
        ;
    };

答案 1 :(得分:0)

回答标题问题:不,不可能知道枚举是否具有显式的基础类型。

即使有,它也不能解决您的实际问题,更像是“如何知道枚举类型的大小是否固定?”

想象一个简单的例子:

enum class Foo : long {};

在某些系统上,这将是32位,而在其他系统上,它将是64位。因此,即使某种机制让您发现它具有显式类型,它也无济于事,因为它的大小不可移植。