能否确定一个枚举类型是强类型的?

8

C++11引入了两种不同的枚举处理方法:一个是让它们有作用域,另一个是让它们有类型。因此,现在我们有四种不同的枚举子类型:

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

这个问题询问如何确定枚举是否有作用域。我想知道如何确定枚举是否有类型。


额外的信息

我使用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 到代码中,如果枚举在声明时没有强类型化,它将会打破编译过程。


你真正想做什么?无论如何,都有方法可以获取底层类型。 - John
3
你认为 enum Old { VALUE = -1ULL; }; 是一种带类型的枚举吗? - NathanOliver
1
我不确定你是否可以检查 enum 是否具有指定类型,但是你可以检查其底层类型是什么:https://en.cppreference.com/w/cpp/types/underlying_type - Ben Jones
1
@VictorGubin 你从哪里得到的这个信息?如果未指定类型,默认情况下,int 只适用于有作用域的枚举。 - NathanOliver
1
@VictorGubin,所以你刚才引用的内容与你之前说的相反。底层类型是一个实现定义的整数类型,可以表示所有枚举器值;除非枚举器的值无法适应int或unsigned int,否则该类型不大于int 意味着根据值的不同,任何从charint的类型都是可接受的。 enum foo { bar }; 合法地允许具有char底层类型。 - NathanOliver
显示剩余6条评论
2个回答

1

std::underlying_type可以用于将编译限制为一组固定宽度整数类型(例如使用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..
        ;
    };

你好,很高兴看到你在为SO做出贡献,但目前这更像是一条评论而不是答案。OP已经链接了你刚刚发布的答案。此外,我在这里只看到一个问题,即标题中提出的:“是否可能确定枚举是否为强类型?”尽管写“你可以使用XXX来做YYY”是技术上正确的答案,但最好提供一些概念证明代码。 - R2RT

0
回答标题问题: 不,无法知道枚举是否具有显式底层类型。
即使有,也无法解决您的实际问题,这更像是“如何知道枚举类型具有固定大小?”
想象一下这个简单的情况:
enum class Foo : long {};

在某些系统上,它将会是32位的,在另一些系统上则会是64位的。因此,即使某种机制让你找出它具有明确的类型,它也无法帮助你,因为大小不可移植。

显然,当指定枚举的基础类型时,用户应该使用固定长度整数。这就是全部意图。像您示例中指定非固定类型是如此荒谬的选择,以至于它的可能性不是问题,不像用户未指定类型的可能性那样。 - ScumCoder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接