C++ 模板化QDataStream运算符<&书信电报;用于枚举
根据这个网站上的各种答案,我试图定义自己的模板函数,将任何枚举值写入QDataStreamC++ 模板化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:
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;
}