是否有可能发现如果用C存在一类++使用SFINAE ? 如果可能的话又如何?
假设我们有一个只能由库的某些版本中提供的类。 我想知道是否可以使用SFINAE检测类是否存在。 检测的结果是任意的,比方说一个枚举常数,如果它存在,其为1,否则为0。
是否有可能发现如果用C存在一类++使用SFINAE ? 如果可能的话又如何?
假设我们有一个只能由库的某些版本中提供的类。 我想知道是否可以使用SFINAE检测类是否存在。 检测的结果是任意的,比方说一个枚举常数,如果它存在,其为1,否则为0。
如果我们要求编译器为我们讲述一个类类型什么T
那甚至还没有被宣布,我们一定会得到一个编译错误。 有没有办法一轮。 因此,如果我们想知道类是否T
“存在”,其中T
甚至可能没有被宣布然而,我们必须声明T
第一。
但是,这是确定的,因为仅仅宣布T
不会使其“存在”,因为我们必须意味着T
存在是T
的定义 。 如果,已宣布T
,你就可以判断它是否已经定义 ,你不需要在任何混淆。
所以,问题是要确定T是否是一个定义的类类型。
sizeof(T)
在这里没有帮助。 如果T
是不确定的,然后它会给出一个incomplete type T
的错误。 同样typeid(T)
这也不是什么好各具特色的类型的SFINAE探头T *
,因为T *
是一个定义的类型,只要T
已经宣布,即使T
不是。 既然我们有责任有类的声明T
, std::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()
在某个给定的A
。 std::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
依然没有找到在这个职位一个令人满意的答案...
麦克金汉开始正确的答案,并告诉一个聪明的事情:
所以,问题是要确定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;
};
随着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;
演示。
好吧,我想我找到了一个办法做到这一点,尽管可能会有更好的办法。 假设我们有包括在库中的某些情况下,而不是在其他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。不幸的是,这仍然需要一个类的前向声明在这两种情况下进行编译。 我不明白的方式来避免这种情况。