如何检测使用SFINAE一类的存在?如何检测使用SFINAE一类的存在?(How to detect

2019-05-14 10:21发布

是否有可能发现如果用C存在一类++使用SFINAE ? 如果可能的话又如何?

假设我们有一个只能由库的某些版本中提供的类。 我想知道是否可以使用SFINAE检测类是否存在。 检测的结果是任意的,比方说一个枚举常数,如果它存在,其为1,否则为0。

Answer 1:

如果我们要求编译器为我们讲述一个类类型什么T那甚至还没有被宣布,我们一定会得到一个编译错误。 有没有办法一轮。 因此,如果我们想知道类是否T “存在”,其中T甚至可能没有被宣布然而,我们必须声明T第一。

但是,这是确定的,因为仅仅宣布T不会使其“存在”,因为我们必须意味着T存在T的定义 。 如果,已宣布T ,你就可以判断它是否已经定义 ,你不需要在任何混淆。

所以,问题是要确定T是否是一个定义的类类型。

sizeof(T)在这里没有帮助。 如果T是不确定的,然后它会给出一个incomplete type T的错误。 同样typeid(T) 这也不是什么好各具特色的类型的SFINAE探头T * ,因为T * 一个定义的类型,只要T已经宣布,即使T不是。 既然我们有责任有类的声明Tstd::is_class<T>不是答案要么,因为该声明就足以为它说“是”。

C ++ 11提供std::is_constructible<T ...Args><type_traits> 这可以提供一个现成的PEG溶液? -因为如果T被定义,则它必须具有至少一个构造函数。

我不是冒牌货。 如果你知道的至少一个公共构造方法的签名T然后GCC的<type_traits>如4.6.3)的确会做生意。 说一个已知的公共构造函数是T::T(int) 然后:

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

如果为真T定义和虚假如果T仅仅声明。

但是,这是不可移植。 <type_traits>在VC ++ 2010还没有提供std::is_constructible甚至它std::has_trivial_constructor<T>如果将BARF T没有定义:当最有可能std::is_constructible没有到达它会效仿。 此外,在该不测只有私有构造T的提供存在std::is_constructible那么即使GCC将BARF(这是眉认识)。

如果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必须有一个公共析构函数中的参数表达式合法调用decltype(std::declval<A>().~A())has_destructor<T>是我作出贡献的方法-内省模板的简化适应此处 。)

这一论点表达的意思std::declval<A>().~A()可以是模糊的一些,具体std::declval<A>() 函数模板std::declval<T>()中定义<type_traits>并返回一个T&& (右值-参照T ) -尽管它也可以仅在未计算的上下文中,如的自变量调用decltype 。 因此意义std::declval<A>().~A()调用~A()在某个给定的Astd::declval<A>()以避免需要那里是任何公共构造以及在此提供我们T ,还是让我们了解它。

因此,SFINAE探针“是过载”的变量类型是: 指针的析构函数的类型A ,并test<T>(0)将匹配过载以防万一有这样的类型的析构A ,对A = T

随着has_destructor<T>在手-和限制的公开破坏值T牢牢记住-你可以测试是否类T在一些点在你的代码,确保你问这个问题之前声明它定义。 这是一个测试程序。

#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


Answer 2:

依然没有找到在这个职位一个令人满意的答案...

麦克金汉开始正确的答案,并告诉一个聪明的事情:

所以,问题是要确定T是否是一个定义的类类型。

的sizeof(T)是没有帮助这里

是不正确的...

这里是你如何可以做到这一点sizeof(T)

template <class T, class Enable = void>
struct is_defined
{
    static constexpr bool value = false;
};

template <class T>
struct is_defined<T, std::enable_if_t<(sizeof(T) > 0)>>
{
    static constexpr bool value = true;
};


Answer 3:

随着SFINAE,没有。 我觉得名字查找招数来完成这件事的方式。 如果你不怕注入一个名字到库的命名空间:

namespace lib {
#if DEFINE_A
class A;
#endif
}

namespace {
    struct local_tag;
    using A = local_tag;
}

namespace lib {
    template <typename T = void>
    A is_a_defined();
}

constexpr bool A_is_defined =
  !std::is_same<local_tag, decltype(lib::is_a_defined())>::value;

演示。

如果A在全局命名空间中声明:

#if DEFINE_A
class A;
#endif

namespace {
    struct local_tag;
    using A = local_tag;
}

namespace foo {
    template <typename T = void>
    ::A is_a_defined();
}

constexpr bool A_is_defined =
  !std::is_same<local_tag, decltype(foo::is_a_defined())>::value;

演示。



Answer 4:

好吧,我想我找到了一个办法做到这一点,尽管可能会有更好的办法。 假设我们有包括在库中的某些情况下,而不是在其他A级。 关键是要定义一个特殊的专用转换构造函数,然后使用SFINAE检测转换构造函数。 当包括A,检测成功; 当它不是,检测失败。

这里有一个具体的例子。 首先对检测模板头,class_defined.hpp:

struct class_defined_helper { };

template< typename T >
struct class_defined {

  typedef char yes;
  typedef long no;

  static yes test( T const & );
  static no  test( ... );

  enum { value = sizeof( test( class_defined_helper( )) == sizeof( yes ) };
};

#define CLASS_DEFINED_CHECK( type )     \
  type( class_defined_helper const & ); \
                                        \
  friend struct class_defined< type >;

现在,它包含一个类定义,blah.hpp头:

#include "class_defined.hpp"

#ifdef INCLUDE_BLAH
class blah {
  CLASS_DEFINED_CHECK( blah );
};
#else
class blah;
#endif

现在的源文件,main.cpp中:

#include "blah.hpp"

int main( ) {
  std::cout << class_defined< blah >::value << std::endl;
}

与BLAH_INCLUDED编译定义此打印1.在不BLAH_INCLUDED定义它打印0。不幸的是,这仍然需要一个类的前向声明在这两种情况下进行编译。 我不明白的方式来避免这种情况。



文章来源: How to detect existence of a class using SFINAE?