用于分类抽象类的模板语法

Template syntax for classifying abstract classes

本文关键字:语法 分类 抽象类 用于      更新时间:2023-10-16

在浏览一些代码时,我遇到了以下模板:

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)将被重载调用,该重载为非抽象( is_abstract<SomeAbstractClass>::result = true)类和其他重载调用No,否则,为什么?

方法签名中的U (*)[1]语法是什么?

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)有问题:

  1. 您需要调用test<T>(T()),这对于任何抽象类T(不仅声明,而且调用)来说都是病态的,因此SFINAE将无法工作(因为代码在"之前"是病态的);
  2. U*总是有效的,即使是抽象类,所以这是不可以的;
  3. 可能是这里更简单的解决方案,因为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_*类,您应该遵循这个示例,而不是您找到的

U (*)[1]是一个指向U实例的数组指针。
由于抽象类不能被实例化,这样的数组将是错误的,因此重载将被忽略(这是SFINAE),并且调用返回到(...)重载。