首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >检查类型是否声明为元类型系统(用于SFINAE)

检查类型是否声明为元类型系统(用于SFINAE)
EN

Stack Overflow用户
提问于 2013-04-16 09:44:43
回答 1查看 1.9K关注 0票数 6

要使用SFINAE对参数t进行T类型的区分,我想知道语句是否

代码语言:javascript
运行
复制
QVariant::fromValue(t);

和/或

代码语言:javascript
运行
复制
QVariant::value<T>();

编译。如果编译,则另一个也会编译,除非您破解了元类型系统。它们编译的当且仅当T是使用Q_DECLARE_METATYPE(T)声明的。

非常简单的用法示例,其中想要通过简单的qDebugging打印值的类型--一个变体包装的等价物,当且仅当元类型系统支持它(我不需要这个,但这在一个最小的例子中显示了问题):

代码语言:javascript
运行
复制
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版本之间发生变化。

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

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

“不可移植”的方法是在一个QMetaTypeId2<T>::Defined中使用enable_if的内部定义(它是定义为0或1的枚举值)。因此,一个可行的解决办法是:

代码语言:javascript
运行
复制
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没有文档化,而且只是内部的东西,所以它不应该出现在客户端代码中。

EN

回答 1

Stack Overflow用户

发布于 2013-04-16 20:39:17

你应该声明一个能帮助你的包装器。我认为最好是基于Qt的版本有几个定义:

代码语言:javascript
运行
复制
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给出了他(现在已删除)答案不会像预期的那样工作

守则是:

代码语言:javascript
运行
复制
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>()的编译错误来自于函数的主体,而不是它的原型(顺便说一句,只有当decltype中的函数被声明并正确调用时,代码才会编译,例如,没有非模板函数的模板参数)。

因此,由于test()的重载比变量重载更具体,所以总是会选择它,因此它将始终“返回”注册类型;您可以在以下测试代码中看到它:

代码语言:javascript
运行
复制
// ----------------------------------------------------------
// 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的信息。另外,你也可以这里

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16033719

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档