如果我们要求编译器告诉我们关于类类型 T 的任何信息
即使被声明,我们也一定会得到一个编译错误。没有办法
周围。因此,如果我们想知道T 类是否“存在”,T
可能还没有声明,我们必须先声明T。
但这没关系,因为仅仅声明T 不会让它“存在”,因为
T 存在 必须是指T 已定义。如果,声明T,
然后你可以确定它是否已经定义,你不需要在
任何混乱。
所以问题是判断T是否是一个已定义的类类型。
sizeof(T) 在这里没有帮助。如果T 未定义,那么它将给出一个
incomplete type T 错误。同样typeid(T)。也没什么好处
在 T * 类型上制作 SFINAE 探针,因为 T * 是 已定义类型
只要 T 已声明,即使 T 未声明。既然我们是
必须声明类T,std::is_class<T> 不是
要么回答,因为该声明足以让它说“是”。
C++11 在<type_traits> 中提供了std::is_constructible<T ...Args>。能
这提供了一个现成的解决方案? - 如果定义了T,那么它必须至少有一个构造函数。
恐怕不会。如果您知道至少一位公众的签名
T 的构造函数,然后 GCC 的 <type_traits>(截至 4.6.3)确实可以
这生意。假设一个已知的公共构造函数是T::T(int)。那么:
std::is_constructible<T,int>::value
如果定义了T,则为真,如果仅声明T,则为假。
但这不是便携式的。 VC++ 2010 中的<type_traits> 还没有提供std::is_constructible,如果T 没有定义,甚至它的std::has_trivial_constructor<T> 也会失败:很可能当std::is_constructible 到达时它会效仿。此外,如果只有 T 的私有构造函数存在用于提供给 std::is_constructible 然后甚至是 GCC
会吐(这是挑眉)。
如果定义了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() 的含义是在给定A 上调用~A()。 std::declval<A>() 在这里为我们提供了很好的服务,因为它不需要T 的任何公共构造函数,或者让我们知道它。
因此,“Yes 重载”的 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