报错代码

对于下面的代码:

template <typename T>
class AbstractContainer {
    ...
protected:
    T *_elements;
    size_t _size;
    size_t _capacity;
}

template <typename T>
class Vector final: public AbstractContainer<T> {
public:
    ...
    int size() {
        return _size; //编译报错
    }
}

class String final: public AbstractContainer<char> {
public:
    ...
    int size() {
        return _size; //编译通过
    }
}

如果一个模板类继承自其一个模板父类(Vector类),那么这个模板类将无法直接访问来自父类(AbstractContainer)的成员,编译器将报错:error: '_size' was not declared in this scope。然而,如果对于继承了确定类型父类的非模板类(String类),却没有这样的问题。
按理来说,子类应该可以正确地读取来自父类的protected成员才对,为什么模板类不能读取呢?

原因分析

参考C++11标准的14.6.2第三条:

In the definition of a class or class template, if a base class depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.

也就是说,对于一个模板类,如果父类的定义依赖于模板参数,在进行非限定名称查找时,将不会查找父类的作用域

C++的两阶段查找

上面的标准中提到,C++的名称查找分为两个阶段:

阶段一:模板定义阶段

模板参数确定后才能解析的名称称为依赖名,反之,如果一个名称的解析不依赖于模板参数,则称为非依赖名
在模板定义的阶段,编译器将对非依赖名进行查找。

阶段二:模板实例化阶段

对于依赖名,由于依赖于具体的变量类型,因此在模板实例化的阶段才进行名称查找。

解决办法

有两个解决办法:

限定名称查找

既然这个标准是针对非限定名称查找的,那我限定名称查找的范围不就好了 quyinniang_huaji.png

template<typename T>
int Vector<T>::size() {
    return AbstractContainer<T>::_size;
}

明确声明成员名是依赖名

如果明确告诉编译器这个成员是依赖名,那么依据C++11标准的14.6.2第一条:

Such names are unbound and are looked up at the point of the template instantiation (14.6.4.1) in both the context of the template definition and the context of the point of instantiation.

编译器将在模板类中寻找该成员。

template<typename T>
int Vector<T>::size() {
    return this->_size;
}

由于this依赖于模板参数,因此this->_size将被编译器认为是一个依赖名,并在上述的阶段二:模板实例化阶段进行名称查找,从而正确地从模板父类中找到成员。

写在最后

我不太能理解这个标准的意义是什么 quyinniang_chigua.png 感觉徒增麻烦...

本文作者:小欢

本文链接:C++模板类 无法找到父类成员 - https://www.xh-ws.com/archives/cpp-template-name-lookup.html

版权声明:如无特别声明,本文即为原创文章,仅代表个人观点,版权归 小欢博客 所有,遵循知识共享署名-相同方式共享 4.0 国际许可协议。转载请注明出处!