如何使用SFINAE检测类的存在? [英] How to detect existence of a class using SFINAE?

查看:127
本文介绍了如何使用SFINAE检测类的存在?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否可以使用 SFINAE 检测C ++中是否存在类?如果可能的话,如何?



假设我们有一个类只由一些库的版本提供。我想知道是否可以使用SFINAE来检测类是否存在。检测结果是任意的,比如一个枚举常数,如果存在则为1,否则为0.如果我们要求编译器告诉我们关于一个类类型 T 没有
甚至被声明我们必然会得到一个编译错误。有没有办法
轮。因此,如果我们想知道类 T 是否存在,其中 T
可能甚至声明,我们必须先声明 T



但是没关系,因为只需声明 T 不会使它存在,因为
我们必须通过 T T 已定义。如果已经声明 T
,就可以确定它是否已经定义
,您不需要在
任何混淆。



所以问题是确定T是否是定义的类类型。



sizeof(T)在这里没有帮助。如果 T 未定义,那么它将给出
不完整类型T 错误。同样 typeid(T)。也没有什么好的
在类型 T * 上创建SFINAE探针,因为 T * T 已声明,即使 T 不是。因为我们是
必须有一个 T std :: is_class< T> 也不是
回答,因为该声明就足以说是。



C ++ 11在 std :: is_constructible< T ... Args> >< type_traits> 。可以
这提供了一个离线解决方案? - 如果定义 T ,那么它必须
至少有一个构造函数。



'm fraid不。如果你知道 T 的至少一个public
构造函数的签名,那么GCC的< type_traits> 从4.6.3)将确实做bb b业务。说一个已知的公共构造函数是 T :: T(int)。然后:

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

如果 T 被定义,则为true,如果 T 只是声明。



但这不是便携式的。在VC ++ 2010中的< type_traits> 尚未提供
std :: is_constructible code> std :: has_trivial_constructor< T> 将
barf如果 T 未定义:最有可能当 std :: is_constructible 到达
它会跟随。此外,在提供给 std :: is_constructible 的私有
构造函数 T 甚至GCC
将barf(这是眉毛提高)。



如果定义 T 有一个析构函数,并且只有一个析构函数。和
析构函数比任何其他可能的成员 T 更可能是公共的。在
中,我们可以做的最简单和最强大的工作是为 T ::〜T 的存在创建一个
SFINAE探针。 / p>

此SFINAE探针不能以常规方式制作以确定
是否 T 具有普通成员函数 mf - 使SFINAE探测函数的Yes overload
接受一个参数,它定义在 code>& T :: mf 。因为我们不允许使用
析构函数(或构造函数)的地址。



但是,如果 T 定义,则 T ::〜T 有一个类型 DT - 必须是
每当 dt 是一个计算
调用的表达式时, decltype(dt) c $ c> T ::〜T
;因此 DT * 将是一个类型,也可以在
中给出作为函数重载的参数类型的原则。因此,我们
可以这样编写探针(GCC 4.6.3):

  #ifndef HAS_DESTRUCTOR_H 
#define HAS_DESTRUCTOR_H

#include< type_traits>

/ *!模板`has_destructor< T>```````
布尔常数`值为true iff`T`有
公共析构函数。

N.B.如果T具有非公开析构函数,将发生编译错误。
* /
template<类型名T>
struct has_destructor
{
/ * has destructor :) * /
template< typename A>
static std :: true_type test(decltype(std :: declval< A>()。〜A())*){
return std :: true_type
}

/ *没有析构函数(* /
template< typename A>
static std :: false_type test(...){
return std :: false_type();
}

/ *这将是`std :: true_type`或`std :: false_type` * /
typedef decltype测试< T>(0))类型;

static const bool value = type :: value; / *这是什么?* /
};

#endif // EOF

仅限于 T 必须在 decltype(std :: declval< A>()。)的参数表达式中合法地调用
的一个 public 析构函数。 )

has_destructor< T> 是方法内省的简化修改
template < =http://stackoverflow.com/a/10707822/1362568>此处。)



该参数表达式的含义 std :: declat< A>()。〜A()可能是
对某些模糊,具体地 code>。在< type_traits> 中定义了函数模板
std :: declval< T>()返回
T&&& (rvalue-引用 T ) - 尽管它只能被调用在未评估
上下文中,例如 decltype 的参数。所以
std :: declval< A>()。〜A()的含义是〜A () A
std :: declval< A>()通过消除需要
任何公共构造函数<$ c $



因此,Yes overload的SFINAE探针的参数类型是:
指向 A test (0)的析构函数类型的指针将匹配
重载,以防万一有 A 的析构函数 c $ c> A = T



使用 has_destructor< ; T> 在手 - 并且它的限制公开破坏
T 牢记 - 你可以测试一个类 T 定义在
代码中的某一点,通过确保在请求
之前先声明
。这是一个测试程序。

  #includehas_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 //定义
具有析构函数,而2 //未定义类则不会。第五个
行输出将说明 int 是可破坏的,最后的
行将显示 std :: has_trivial_destructor< ; int> 同意。如果我们想要
将字段缩小到类类型,则可以在
之后应用 std :: is_class ,我们确定 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


Is it possible to detect if a class exists in C++ using SFINAE? If possible then how?

Suppose we have a class that is provided only by some versions of a library. I'd like to know if it is possible to use SFINAE to detect whether the class exists or not. The result of detection is arbitrary, say an enum constant which is 1 if it exists, 0 otherwise.

解决方案

If we ask the compiler to tell us anything about a class type T that has not even been declared we are bound to get a compilation error. There is no way round that. Therefore if we want to know whether class T "exists", where T might not even have been declared yet, we must declare T first.

But that is OK, because merely declaring T will not make it "exist", since what we must mean by T exists is T is defined. And if, having declared T, you can then determine whether it is already defined, you need not be in any confusion.

So the problem is to determine whether T is a defined class type.

sizeof(T) is no help here. If T is undefined then it will give an incomplete type T error. Likewise typeid(T). Nor is it any good crafting an SFINAE probe on the type T *, because T * is is a defined type as long as T has been declared, even if T isn't. And since we are obliged to have a declaration of class T, std::is_class<T> is not the answer either, because that declaration will suffice for it to say "Yes".

C++11 provides std::is_constructible<T ...Args> in <type_traits>. Can this offer an off-the-peg solution? - given that if T is defined, then it must have at least one constructor.

I'm fraid not. If you know the signature of at least one public constructor of T then GCC's <type_traits> (as of 4.6.3) will indeed do the business. Say that one known public constructor is T::T(int). Then:

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

will be true if T is defined and false if T is merely declared.

But this isn't portable. <type_traits> in VC++ 2010 doesn't yet provide std::is_constructible and even its std::has_trivial_constructor<T> will barf if T is not defined: most likely when std::is_constructible does arrive it will follow suit. Furthermore, in the eventuality that only private constructors of T exist for offering to std::is_constructible then even GCC will barf (which is eyebrow raising).

If T is defined, it must have a destructor, and only one destructor. And that destructor is likelier to be public than any other possible member of T. In that light, the simplest and strongest play we can make is to craft an SFINAE probe for the existence of T::~T.

This SFINAE probe cannot be crafted in the routine way for determining whether T has an ordinary member function mf - making the "Yes overload" of the SFINAE probe function take an argument that is defined in terms of the type of &T::mf. Because we're not allowed to take the address of a destructor (or constructor).

Nevertheless, if T is defined, then T::~T has a type DT- which must be yielded by decltype(dt) whenever dt is an expression that evaluates to an invocation of T::~T; and therefore DT * will be a type also, that can in principle be given as the argument type of a function overload. Therefore we can write the probe like this (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

with only the restriction that T must have a public destructor to be legally invoked in the argument expression of decltype(std::declval<A>().~A()). (has_destructor<T> is a simplified adaptation of the method-introspecting template I contributed here.)

The meaning of that argument expression std::declval<A>().~A() may be obscure to some, specifically std::declval<A>(). The function template std::declval<T>() is defined in <type_traits> and returns a T&& (rvalue-reference to T) - although it may only be invoked in unevaluated contexts, such as the argument of decltype. So the meaning of std::declval<A>().~A() is a call to ~A() upon some given A. std::declval<A>() serves us well here by obviating the need for there to be any public constructor of T, or for us to know about it.

Accordingly, the argument type of the SFINAE probe for the "Yes overload" is: pointer to the type of the destructor of A, and test<T>(0) will match that overload just in case there is such a type as destructor of A, for A = T

With has_destructor<T> in hand - and its limitation to publicly destructible values of T firmly in mind - you can test whether a class T is defined at some point in your code by ensuring that you declare it before asking the question. Here is a test program.

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

Built with GCC 4.6.3, this will tell you that the 2 // Defined classes have destructors and the 2 // Undefined classes do not. The fifth line of output will say that int is destructible, and the final line will show that std::has_trivial_destructor<int> agrees. If we want to narrow the field to class types, std::is_class<T> can be applied after we determine that T is destructible.

Visual C++ 2010 does not provide std::declval(). To support that compiler you can add the following at the top of has_destructor.h:

#ifdef _MSC_VER
namespace std {
template <typename T>
typename add_rvalue_reference<T>::type declval();
}
#endif

这篇关于如何使用SFINAE检测类的存在?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆