6

为了区分使用 SFINAEt类型的参数的大小写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 版本之间发生变化。

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

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

“不可移植”的方式是使用QMetaTypeId2<T>::Definedin an的内部定义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没有记录并且只是内部的东西,它不应该出现在客户端代码中。

4

1 回答 1

1

您应该声明一个可以帮助您的包装器。我能想到的最好的方法是根据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
    };
};

这不是美学,但这是功能性的,在您的代码中您可以使用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))
{
};

为什么这行不通?其目的是因为调用qMetaTypeId<T>()未注册的类型会导致编译错误,“SFINAE 将在未注册类型时排除第一个函数”。这里的问题是它qMetaTypeId<T>()始终是一个有效的表达式,qMetaTypeId<T>(), std::true_type()也是如此,并且decltype(qMetaTypeId<T>(), std::true_type())是完美定义的(使用 value std::true_type)。
这是因为 stem 的编译错误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);
}

您可能想了解更多关于 SFINAE的信息。此外,您可以在此处阅读qmetatype.h

于 2013-04-16T20:39:17.263 回答