C++ 模板化QDataStream运算符<&书信电报;用于枚举

C++ 模板化QDataStream运算符<&书信电报;用于枚举,c++,qt,C++,Qt,根据这个网站上的各种答案,我试图定义自己的模板函数,将任何枚举值写入QDataStream template <typename T, typename std::enable_if_t<std::is_enum<T>::value>> QDataStream &operator<<(QDataStream& stream, T enumValue) { stream << static_cast<std:

根据这个网站上的各种答案,我试图定义自己的模板函数,将任何枚举值写入QDataStream

template <typename T, typename std::enable_if_t<std::is_enum<T>::value>>
QDataStream &operator<<(QDataStream& stream, T enumValue)
{
    stream << static_cast<std::underlying_type_t<T>>(enumValue);
    return stream;
}

enum class MyEnum_e : int16_t{};

QDataStream stream;
MyEnum_e value;
stream << value; // Doesn't work
模板

QDataStream&operator您需要
启用\u if\t
作为模板参数的默认值

template <typename T, typename U=std::enable_if_t<std::is_enum<T>::value>>
QDataStream &operator<<(QDataStream& stream, T enumValue)
{
    stream << static_cast<std::underlying_type_t<T>>(enumValue);
    return stream;
}
模板
QDataStream和操作符解决方案是C++14。以下解决方案适用于C++11

1。根据阿斯切普勒的回答:

template <typename T, typename U=std::enable_if<std::is_enum<T>::value>>
QDataStream &operator<<(QDataStream& stream, T enumValue)
{
    using underlying_type_t = typename std::underlying_type<T>::type;
    stream << static_cast<underlying_type_t>(enumValue);
    return stream;
}

请参阅此处需要
typename std::enable_if::type
,因为需要返回该类型。

因为我在尝试将
枚举类型:quint8{…}
流式传输到
QSettings
时发现了这一点,所以这里有两种完整性方法


模板
QDataStream&运算符>val;
枚举值=静态_转换(val);
回流;
}
同样对于
QSettings
而言,必须在Qt元对象系统中注册
enum
类型和流操作符。枚举使用
Q_ENUM()
(在
QObject
Q_GADGET
中)或
Q_ENUM()
(在名称空间中)注册,或在其他情况下使用
QT_DECLARE_METATYPE()
注册。流操作符需要单独注册到
qRegisterMetaTypeStreamOperators(“Type”)
。还请注意,对于
QFlags
已经定义了流式操作符,但是它们仍然需要向
qRegisterMetaTypeStreamOperators注册才能自动流式处理

更新:
带有C++14的MSVC17不喜欢我上面的代码,因此根据@DrumM的回答,以下是我现在使用的代码,MinGW 7.3、gcc 6.3和7.3:

模板
typename std::enable_if::type
运算符>val;
枚举值=静态_转换(val);
回流;
}
// ... 后来
qRegisterMetaTypeStreamOperators(“MyScope::MyEnum”);

只是一个问题,为什么需要
U=
?这很奇怪,因为它没有在函数体中使用。如果没有,则当
T
实际上是枚举类型时,
std::enable_如果\u T
计算为类型
void
,则会失败,错误为
非类型模板参数的非法类型。如果没有
U=
,这将被解析为试图声明类型为
void
的非类型模板参数(接受编译时值而不是类型的参数)——但这是非法的,因为没有类型为
void
的值。
U=
意味着我们要声明一个类型参数,该类型的默认参数是
enable\U if\t
thing,它被允许是
void
@drum另一个有效的方法是
template
*
使非类型模板参数的类型
void*
而不是
void
,这是一个有效的类型。然后我们想为该参数提供一个默认值,因为编译器无法自动推断它,所以我们只需添加
=nullptr
。感谢您提供的信息!第二个模板参数(
U
)仅用于确保模板仅在其类型为
enum
时执行?比我想的要多;-)@DrumM Right—虽然从技术上讲,它可以确保模板不会在编译时生成可行的重载解析候选函数。执行更像是运行时的事情。这称为SFINAE技术-替换失败不是错误。幸运的是,在C++20中,我们不需要这样的技巧:我们只需编写
template requires std::is_enum_v
,或者如果
enum
被定义为一个概念,我们甚至只需编写
template
。答案1中的运算符实际上从未“禁用”专门化。在中,模板运算符不幸地比预期的
std::string
重载匹配得更好,尽管该参数肯定不是枚举<如果
是一个格式良好且完整的类型,而恰好没有名为
类型
的成员,则启用。您需要通过使用
typename std::enable_if::type
(C++11或更高版本)或
std::enable_if_t::type
(C++14或更高版本)获得SFINAE工作的实际故障。感谢@aschepler提供的宝贵意见!这是有道理的!
template <typename T>
typename std::enable_if<std::is_enum<T>::value, QDataStream&>::type
operator<<(QDataStream& stream, T enumValue)
{
    using underlying_type_t = typename std::underlying_type<T>::type;
    stream << static_cast<underlying_type_t>(enumValue);
    return stream;
}