9

I decided to test one of the examples in "Effective C++" and I'm not getting the result I expected. So, apparently this (simplified) code shouldn't compile:

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

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

Here's the explanation (class names changed for simplicity) :

The code above won't compile, at least not with conformant compilers. Such compilers will complain that f doesn't exist. We can see that f is in the base class, but compilers won't look for it there.

We need to understand why. The problem is that when compilers encounter the definition for the class template B, they don't know what class it inherits from. Sure, it's A<T>, but T is a template parameter, one that won't be known until later (when B is instantiated). Without knowing what T is, there's no way to know what the class A<T> looks like. In particular, there's no way to know if it has a f function.

My compiler (Visual Studio) doesn't mind... It doesn't even show any warnings.

Is the above code correct or not?

4

1 回答 1

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

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

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

解决方案是使表达式依赖,最简单的方法是使用 限定this->

template <typename T>
void B<T>::f2() {  this->f(); }

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

template <typename T>
void B<T>::f2() { A<T>::f(); }

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


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

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

于 2013-10-02T03:39:03.063 回答