为什么我可以从派生类调用基本模板类方法

Ole*_*siy 7 c++ inheritance templates base

我决定测试"Effective C++"中的一个例子,但我没有得到我预期的结果.所以,显然这个(简化的)代码不应该编译:

template <class T>
struct A {
    void f(){}
};

template <class T>
struct B : public A <T> {
    void f2() { f(); }   // calling base function - will not compile
};
Run Code Online (Sandbox Code Playgroud)

这是解释(为简单起见改变了类名):

上面的代码不会编译,至少不符合一致的编译器.这样的编译器会抱怨f不存在.我们可以看到它f在基类中,但编译器不会在那里寻找它.

我们需要了解原因.问题是当编译器遇到类模板的定义时B,他们不知道它从哪个类继承.当然,它是A<T>,但它T是一个模板参数,直到稍后(B实例化时)才会知道.不知道是什么T ,没有办法知道这个类是什么A<T>样的.特别是,没有办法知道它是否具有f功能.

我的编译器(Visual Studio)并不介意......它甚至没有显示任何警告.

以上代码是否正确?

Dav*_*eas 8

template <class T>
struct A {
    void f(){}
};

template <class T>
struct B : public A <T> {
    void f2() { f(); }   // calling base function - will not compile
};
Run Code Online (Sandbox Code Playgroud)

在派生模板中,表达式f()不依赖于任何模板参数,因此编译器会在第一阶段查找期间尝试解析它.此时,模板尚未使用类型进行实例化,编译器不会查看基础A<T>.原因是编译器无法知道实例化的类型是否存在A<T>可能不包含任何成员的特化.

解决方案是使表达式依赖,最简单的方法是符合以下条件this->:

template <typename T>
void B<T>::f2() {  this->f(); }
Run Code Online (Sandbox Code Playgroud)

由于表达式现在是依赖的,因此查找将延迟到第二阶段,其中类型被替换并且A<T>是具体类型.另一种选择是使用定义它的类进行限定:

template <typename T>
void B<T>::f2() { A<T>::f(); }
Run Code Online (Sandbox Code Playgroud)

表达式再次变得依赖,并将在第二阶段解决.主要区别在于,在第二种情况下,调用是合格的,因此它不使用动态调度.如果A<T>::f()是虚拟的,它仍将执行A<T>::f(),而不是最终的覆盖.


代码是否正确?不,VS接受吗?是.

这是Visual Studio编译器中已知的不一致,它不实现两阶段查找.它将模板内的所有查找延迟到第二阶段,此时查找成功.