如果我们要求编译器告诉我们有关类类型的任何信息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