【问题标题】:Is it possible to determine if an enumeration was strongly typed?是否可以确定枚举是否为强类型?
【发布时间】:2019-01-24 20:49:05
【问题描述】:

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,如果枚举在声明时不是强类型,这将中断编译过程。

【问题讨论】:

  • 你真正想做什么?无论如何,都有一些方法可以获取底层类型。
  • 你认为enum Old { VALUE = -1ULL; }; 是一个类型化的枚举吗?
  • 我不知道你是否可以检查enum是否有指定类型,但你可以检查底层类型是什么:en.cppreference.com/w/cpp/types/underlying_type
  • @VictorGubin 你从哪里得到的? int 默认情况下仅适用于未指定类型的挖掘枚举。
  • @VictorGubin 所以你刚才引用的与你之前所说的相反。 底层类型是实现定义的整数类型,可以表示所有枚举值;此类型不大于 int,除非枚举器的值不能适合 int 或 unsigned int 表示从 charint 的任何值都可以接受,具体取决于值。法律允许enum foo { bar }; 具有char 的基础类型。

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


【解决方案1】:

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

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

想象一下这个简单的案例:

enum class Foo : long {};

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

【讨论】:

  • 显然用户在指定枚举的基础类型时应该使用固定长度的整数。这就是重点。在您的示例中指定非固定类型是一种荒谬的选择,它的可能性不是问题,不像用户没有指定类型的可能性。
【解决方案2】:

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

【讨论】:

  • 嗨,很高兴你为 SO 做出贡献,但现在这更像是评论,而不是答案。 OP 已经链接了您刚刚发布的答案。另外,我在这里看不到两个问题,只有标题中提出的一个问题:“是否可以确定枚举是否为强类型?”。尽管写“你可以用 XXX 做 YYY”在技术上是正确的答案,但最好提供一些概念验证代码。
猜你喜欢
  • 2012-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多