为了区分使用SFINAE的t类型的参数T,我想知道该语句是否

QVariant::fromValue(t);

和/或
QVariant::value<T>();

编译。如果一个编译,除非您修改了元类型系统,否则另一个也可以编译。当且仅当使用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>();
}

我知道这样做的几种(但还是类似的)可能性,但是它们都引入了一些辅助结构,复杂的enable_if等。现在我知道有QTypeInfo,我猜它已经提供了类似的内容,例如“在Qt元类型中声明了”系统”类型特征。但是,该类未记录在文档中,因此不建议将其用于长期有效的代码中,因为它可能会在Qt版本之间进行更改。

如果QVariant支持T类型,是否有一种非常简单的方法(比使用“checker” + enable_if简单)来检查SFINAE特化?

请注意,该解决方案仍应在不同的Qt版本之间可移植(Qt4和Qt5可能使用不同的QTypeInfo定义)。但是,我使用C++ 11,因此例如可以访问std::enable_if

“非便携式”方法是在QMetaTypeId2<T>::Defined中使用enable_if的内部定义(这是一个定义为0或1的枚举值)。因此,一个可行的解决方案是:
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>();
}

但是,由于没有记录QMetaTypeId2,而仅记录内部内容,因此它不应出现在客户端代码中。

最佳答案

您应该声明一个可以为您提供帮助的包装器。我能想到的最好的就是根据Qt的version定义几个:

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

那不是美学,而是功能性的,在您的代码中,您可以使用is_registered<T>::value而不用担心Qt的版本。另外,我暂时没有Qt5,所以我不能告诉你QMetaTypeId2<T>::Defined是否正确(尽管我认为是正确的)。

无法使用qMetaTypeId<T>()来检查类型是否已注册。实际上,无论类型如何,qMetaTypeId<T>()表达式始终有效。如果未注册,该函数的主体将不会编译(更精确的说:在当前的Qt 4和5中,qMetaTypeId<T>()仅调用另一个未注册类型的函数,如果未注册该类型,则该代码不起作用)。无法使用SFINAE对其进行测试,因此,代码leemes在其答案will not work as expected(现已删除)中给出了此代码。

该代码是:
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))
{
};

为什么这行不通?目的是因为在未注册的类型上调用qMetaTypeId<T>()会导致编译错误,因此“当未注册类型时,SFINAE将排除第一个函数”。这里的问题是qMetaTypeId<T>()始终是有效的表达式,因此qMetaTypeId<T>(), std::true_type()也是如此,而且decltype(qMetaTypeId<T>(), std::true_type())的定义也很完美(值为std::true_type)。
这是因为qMetaTypeId<T>()的编译错误源于函数的主体,而不是源于其原型(prototype)(通过这种方式,仅当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);
}

您可能想要read more about SFINAE。另外,您可以read qmetatype.h here

09-09 23:53
查看更多