如何使用 SFINAE 检测类的存在?

2023-11-22

是否可以使用 C++ 检测类是否存在SFINAE?如果可以的话怎么办?

假设我们有一个仅由某些版本的库提供的类。我想知道是否可以使用 SFINAE 来检测该类是否存在。检测的结果是任意的,比如一个枚举常量,如果存在则为1,否则为0。


如果我们要求编译器告诉我们有关类类型的任何信息T那还没有 即使已经声明,我们也一定会遇到编译错误。不可能 围绕那个。因此如果我们想知道类是否T“存在”,其中T可能还没有宣布,我们必须宣布T first.

但这没关系,因为只是声明T不会让它“存在”,因为 我们必须说的是什么意思T exists is T被定义为。如果,已经宣布T, 然后你可以确定它是否已经defined,你不必在 任何混乱。

所以问题是判断是否T是一个已定义的类类型。

sizeof(T)这里没有帮助。如果T是未定义的那么它会给出一个incomplete type T错误。同样地typeid(T)。也没有什么好处 在该类型上制作 SFINAE 探针T *, 因为T * is定义的类型 只要T已经宣布,即使T不是。既然我们是 必须有阶级声明T, std::is_class<T>不是 回答其中之一,因为该声明足以使其说“是”。

C++11提供std::is_constructible<T ...Args> in <type_traits>。能 这提供了现成的解决方案吗? - 鉴于如果T被定义,那么它必须至少有一个构造函数。

恐怕不是。如果您知道至少一位公众的签名 的构造函数T然后是海湾合作委员会的<type_traits>(从 4.6.3 开始)确实可以 这生意。假设一个已知的公共构造函数是T::T(int). Then:

std::is_constructible<T,int>::value

为真,如果T已定义且为假,如果T只是声明。

但这不是便携式的。<type_traits>在VC++ 2010中还没有提供std::is_constructible甚至它的std::has_trivial_constructor<T>会呕吐如果T未定义:最有可能的时候std::is_constructible确实到达它也会效仿。此外,万一只有私有构造函数T存在是为了提供std::is_constructible然后甚至海湾合作委员会 会呕吐(即扬起眉毛)。

If T被定义,它必须有一个析构函数,并且只有一个析构函数。并且该析构函数比任何其他可能的成员更有可能是公开的T。有鉴于此,我们能做的最简单、最强大的玩法就是为是否存在制作一个 SFINAE 探测器T::~T.

该 SFINAE 探针无法以常规方式制作来确定 无论T有一个普通的成员函数mf- 使“是”过载 SFINAE 探测函数的参数采用以下术语定义的参数 的的类型&T::mf。因为我们不允许获取地址 析构函数(或构造函数)。

尽管如此,如果T被定义,那么T::~T有一个类型DT- 必须是 产生于decltype(dt)每当dt是一个计算结果为 调用T::~T;因此DT *也将是一种类型,可以在 原则上作为函数重载的参数类型给出。因此我们 可以像这样编写探针(GCC 4.6.3):

#ifndef HAS_DESTRUCTOR_H
#define HAS_DESTRUCTOR_H

#include <type_traits>

/*! The template `has_destructor<T>` exports a
    boolean constant `value that is true iff `T` has 
    a public destructor.

    N.B. A compile error will occur if T has non-public destructor.
*/ 
template< typename T>
struct has_destructor
{   
    /* Has destructor :) */
    template <typename A> 
    static std::true_type test(decltype(std::declval<A>().~A()) *) {
        return std::true_type();
    }

    /* Has no destructor :( */
    template<typename A>
    static std::false_type test(...) {
        return std::false_type(); 
    }

    /* This will be either `std::true_type` or `std::false_type` */
    typedef decltype(test<T>(0)) type;

    static const bool value = type::value; /* Which is it? */
};

#endif // EOF

只有这样的限制T必须有一个public在参数表达式中合法调用的析构函数decltype(std::declval<A>().~A()). (has_destructor<T>是我贡献的方法内省模板的简化改编here.)

该参数表达式的含义std::declval<A>().~A()对于某些人来说可能会很模糊,特别是std::declval<A>()。函数模板std::declval<T>()定义于<type_traits>并返回一个T&&(右值-引用T) - 尽管它只能在未评估的上下文中调用,例如decltype。所以意思是std::declval<A>().~A() is 打电话给~A()根据一些给定的A. std::declval<A>()通过消除对任何公共构造函数的需要,为我们提供了很好的服务T,或者让我们了解它。

因此,“Yes 过载”的 SFINAE 探针的参数类型为:指向析构函数类型的指针A, and test<T>(0)将匹配该重载,以防万一存在这样的类型作为析构函数A, for A = T.

With has_destructor<T>手中的 - 及其对公开可破坏的价值的限制T牢牢记住——你可以测试一下是否有一个类T通过确保您在代码中的某个时刻定义declare在提问之前。这是一个测试程序。

#include "has_destructor.h"
#include <iostream>

class bar {}; // Defined
template< 
    class CharT, 
    class Traits
> class basic_iostream; //Defined
template<typename T>
struct vector; //Undefined
class foo; // Undefined

int main()
{
    std::cout << has_destructor<bar>::value << std::endl;
    std::cout << has_destructor<std::basic_iostream<char>>::value 
        << std::endl;
    std::cout << has_destructor<foo>::value << std::endl;
    std::cout << has_destructor<vector<int>>::value << std::endl;
    std::cout << has_destructor<int>::value << std::endl;
    std::count << std::has_trivial_destructor<int>::value << std::endl;
    return 0;
}

使用 GCC 4.6.3 构建,这将告诉您 2// Defined类 有析构函数和 2// Undefined类没有。第五 输出行会说int是可破坏的,最后 线将表明std::has_trivial_destructor<int>同意。如果我们想要 将范围缩小到类类型,std::is_class<T>可以在之后应用 我们确定T是可破坏的。

Visual C++ 2010 不提供std::declval()。支持该编译器 您可以在顶部添加以下内容has_destructor.h:

#ifdef _MSC_VER
namespace std {
template <typename T>
typename add_rvalue_reference<T>::type declval();
}
#endif
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 SFINAE 检测类的存在? 的相关文章

随机推荐