lee*_*mes 6 c++ qt sfinae type-traits enable-if
为了使用SFINAE t对类型参数进行区分T,我想知道该语句
QVariant::fromValue(t);
Run Code Online (Sandbox Code Playgroud)
和/或
QVariant::value<T>();
Run Code Online (Sandbox Code Playgroud)
编译.如果一个编译,另一个编译,除非你破解元类型系统.当且仅当T声明已使用时,它们才会编译Q_DECLARE_METATYPE(T).
非常简单的用法示例,其中一个人想要通过简单地qDebugging一个变量包装的等价物来打印一个值的类型,当且仅当元类型系统支持时(我不需要这个,但这显示了一个最小的问题)例):
template<class T> // enable if T NOT registered in the Qt meta type system
void print(const T &t) {
qDebug() << t;
}
template<class T> // enable if T registered in the Qt meta type system
void print(const T &t) {
qDebug() << QVariant::fromValue<T>();
}
Run Code Online (Sandbox Code Playgroud)
我知道有几种(但类似的)可能性,但它们都引入了一些辅助结构,复杂的enable_if等等.现在我知道有QTypeInfo哪些,我想,已经提供了类似于"在Qt元类型系统中声明的东西" "类型特征.但是,此类未记录,因此不建议在长期和高效代码中使用,因为它可能在Qt版本之间发生变化.
如果TQVariant支持某种类型,是否有一种非常简单的方法(比使用"checker"+ enable_if更简单)来检查SFINAE专业化?
请注意,解决方案仍应在不同Qt版本之间移植(Qt4和Qt5可能使用不同的QTypeInfo定义).但是,我使用C++ 11,所以我可以访问std::enable_if例如.
"非便携式"的方法是使用的内部定义QMetaTypeId2<T>::Defined在一个enable_if(它是或1定义为0的枚举值).因此,一个有效的解决方案是:
template<class T>
typename std::enable_if<!QMetaTypeId2<T>::Defined>::type
print(const T &t) {
qDebug() << t;
}
template<class T>
typename std::enable_if<QMetaTypeId2<T>::Defined>::type
print(const T &t) {
qDebug() << QVariant::fromValue<T>();
}
Run Code Online (Sandbox Code Playgroud)
但是,由于QMetaTypeId2没有记录,只有内部资料,因此不应出现在客户端代码中.
您应该声明一个可以帮助您的包装器。我能想到的最好的办法是根据Qt 的版本有几个定义:
template<typename T>
struct is_registered
{
enum
{
value =
#if QT_VERSION >= 0x050000 // Qt 5.0.0
QMetaTypeId2<T>::Defined
#elif QT_VERSION >= 0x040000 // Qt 4.0.0
QMetaTypeId2<T>::Defined
#endif
};
};
Run Code Online (Sandbox Code Playgroud)
这不美观,但很实用,并且在您的代码中您可以使用而is_registered<T>::value不必担心 Qt 的版本。另外,我暂时没有 Qt5,所以我无法告诉你QMetaTypeId2<T>::Defined它是否正确(尽管我认为是正确的)。
无法用于qMetaTypeId<T>()检查类型是否已注册。事实上,qMetaTypeId<T>()无论类型如何,表达式始终有效。如果未注册,函数体将不会编译(更准确地说:在 Qt 4 和 5 中(目前),qMetaTypeId<T>()仅调用另一个在类型未注册时不会编译的函数。因此,您不能使用 SFINAE 进行测试。结果,leemes 在他(现已删除)答案中给出的代码将无法按预期工作。
代码是:
struct _test_is_declared_metatype
{
template<class T>
static auto test(T* t) -> decltype(qMetaTypeId<T>(), std::true_type());
static std::false_type test(...);
};
template<class T>
struct is_declared_metatype : decltype(_test_is_declared_metatype::test<T>(0))
{
};
Run Code Online (Sandbox Code Playgroud)
为什么这行不通?其目的是,因为调用qMetaTypeId<T>()未注册的类型会导致编译错误,“当类型未注册时,SFINAE 将排除第一个函数”。这里的问题是qMetaTypeId<T>()始终是一个有效的表达式,也是如此qMetaTypeId<T>(), std::true_type(),并且decltype(qMetaTypeId<T>(), std::true_type())被完美定义(使用值std::true_type)。
这是因为编译错误qMetaTypeId<T>()源于函数体,而不是其原型(顺便说一句,只有在声明decltype并正确调用函数时,代码才会编译,即非模板函数没有模板参数) )。
因此,由于 的重载test()比可变参数重载更具体,因此它总是会被选择,因此它总是会“返回”该类型已注册;你可以在下面的测试代码中看到它:
// ----------------------------------------------------------
// qmetatype.h simplification -------------------------------
// ----------------------------------------------------------
template<typename T>
struct metatype
{
enum { defined = 0 };
};
template<typename T>
struct metatype2
{
enum { defined = metatype<T>::defined };
static inline int id() { return metatype<T>::id(); }
};
template <typename T>
inline int metatypeId(
T * /* dummy */ = 0
)
{
return metatype2<T>::id();
}
#define register_meta_type( _type_ ) \
template<> \
struct metatype< _type_ > \
{ \
enum { defined = 1 }; \
static int id() \
{ \
/* Run-time registration in Qt */ \
return __COUNTER__; \
}; \
};
// ----------------------------------------------------------
// ----------------------------------------------------------
// ----------------------------------------------------------
class TestA {};
register_meta_type(TestA)
class TestB {};
class TestC {};
register_meta_type(TestC)
class TestD {};
#include <type_traits>
struct _test_is_declared_metatype
{
/*
metatypeId<T>() is always a valid expression. So this overload is
always taken
*/
template<class T>
static auto test(T* t) -> decltype(metatypeId<T>(), std::true_type());
static std::false_type test(...);
};
template<class T>
struct is_declared_metatype : decltype(_test_is_declared_metatype::test<T>(0))
{
};
#include <iostream>
#define PRINT_DEF( _type_ ) std::cout << #_type_ << " registered ? " << is_declared_metatype< _type_ >::value << "\n";
int main()
{
std::cout << std::boolalpha;
PRINT_DEF(TestA);
PRINT_DEF(TestB);
PRINT_DEF(TestC);
PRINT_DEF(TestD);
}
Run Code Online (Sandbox Code Playgroud)
您可能想了解有关 SFINAE 的更多信息。另外,您可以在这里阅读qmetatype.h。