在浏览某些代码时,我遇到了以下模板:
template<typename T>
class is_abstract
{
class No { };
class Yes { No no[3]; };
template<class U>
static No test(U (*)[1]); // not defined
template<class U>
static Yes test(...); // not defined
public:
enum { result = (sizeof(test<T>(0)) == sizeof(Yes)) };
};
我了解test<T>(0)
将通过重载调用,该重载会为非抽象返回No
( is_abstract<SomeAbstractClass>::result
= true )等级和其他超载否则,但为什么?
方法签名中的这个U (*)[1]
语法是什么?
答案 0 :(得分:4)
test<T>(0)
将调用test
的第一个或第二个重载。第二个过载是非常标准的(当第一个过载形成错误时它是后备)。
现在,如何使抽象类的第一个声明格式错误?以下是一些可能的解决方案:
template<class U>
static No test(U); // (1)
template<class U>
static No test(U*); // (2)
template<class U>
static No test(U (*)[1]); // (3)
但(1)
和(2)
存在问题:
test<T>(T())
,这对于任何抽象类T
(不仅是声明,还有调用)都会格式不正确,因此SFINAE不起作用(因为代码格式不正确而且#34;在#34;重载解析之前)不会有后退; U*
始终有效,即使是抽象类,所以这不行; U (*)[1]
不要求你在调用期间实例化T()
但是声明对于抽象类型是不正确的,所以回退到test(...)
的工作原理根据需要。请注意,从C ++ 11开始,标准定义了std::is_abstract
类。
关于is_*
课程的附注:
这可能是一个旧的实现,现在有更简单的方法(从C ++ 11开始)。另请注意,使用value
代替result
会更好(遵循标准惯例)。遵循标准惯例的C ++ 11实现可以是 1 :
template <class T>
std::false_type is_abstract_(T (*)[1]);
template <class T>
std::true_type is_abstract_(...);
template<typename T>
struct is_abstract: decltype(is_abstract_<T>(0)) { };
1 如果您需要测试某个类是否为抽象类,请使用标准std::is_abstract
,但是如果您需要创建自己的is_*
类,您应该按照此示例而不是您找到的示例。
答案 1 :(得分:2)
U (*)[1]
是指向U
的一个实例的数组的指针
由于抽象类不能实现,因此这样的数组将是格式错误的,因此忽略了重载(即SFINAE),并且调用将回退到(...)
重载。