我在尝试将enum Type : quint8 {...}
流化到和从QSettings
中读取时发现了这个问题,为了完整性,以下提供两种解决方法。
template<class T, typename std::enable_if<std::is_enum<T>::value, T>::type* = nullptr>
QDataStream &operator<<(QDataStream& stream, const T &enumValue)
{
return stream << static_cast<std::underlying_type_t<T>>(enumValue);
}
template<class T, typename std::enable_if<std::is_enum<T>::value, T>::type* = nullptr>
QDataStream &operator>>(QDataStream& stream, T &enumValue)
{
std::underlying_type_t<T> val;
stream >> val;
enumValue = static_cast<T>(val);
return stream;
}
对于
QSettings
,必须在Qt元对象系统中注册
enum
类型和流运算符。 使用
Q_ENUM()
(在
QObject
或
Q_GADGET
中)或
Q_ENUM_NS()
(在命名空间中),或在其他情况下使用
QT_DECLARE_METATYPE()
来注册枚举。 流运算符需要使用
qRegisterMetaTypeStreamOperators<Type>("Type")
单独注册。 还要注意,对于
QFlags
已经定义了流运算符,但是据我所知,它们仍然需要使用
qRegisterMetaTypeStreamOperators
进行注册以自动进行流式处理。
更新:
基于@DrumM的答案,MSVC17与C++14不喜欢上面的代码,以下是现在适用于我、MinGW 7.3、gcc 6.3和7.3的代码:
template <typename T>
typename std::enable_if<std::is_enum<T>::value, QDataStream&>::type
operator<<(QDataStream& stream, const T &enumValue)
{
return stream << static_cast<std::underlying_type_t<T>>(enumValue);
}
template <typename T>
typename std::enable_if<std::is_enum<T>::value, QDataStream&>::type
operator>>(QDataStream& stream, T &enumValue)
{
std::underlying_type_t<T> val;
stream >> val;
enumValue = static_cast<T>(val);
return stream;
}
qRegisterMetaTypeStreamOperators<MyScope::MyEnum>("MyScope::MyEnum");
T
实际上是一个枚举类型时,std::enable_if_t<std::is_enum<T>::value>
将评估为类型void
。如果没有U=
,这将被解析为尝试声明非类型模板参数(接受编译时值而不是类型的参数)的类型void
- 但这是非法的,因为没有类型为void
的值。U=
意味着我们正在声明一个类型参数,并且该类型的默认参数是enable_if_t
,它允许是void
。 - ascheplertemplate <typename T, std::enable_if_t<std::is_enum<T>::value>* = nullptr>
。*
使得非类型模板参数的类型为void*
,而不是void
,这是一种有效的类型。然后我们想要为该参数提供默认值,因为编译器没有自动推断的方法,所以我们只需添加= nullptr
即可。 - ascheplerU
)只是用来确保模板仅在其类型为“枚举”时执行?那我明白了,我想;-) - jaques-samtemplate <typename T> requires std::is_enum_v<T>
,或者甚至只需编写template <Enum T>
,如果Enum
已被定义为概念。 - aschepler