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



我决定测试"有效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
};

以下是解释(为简单起见,类名已更改(:

上面的代码不会编译,至少不能使用符合标准的编译器。这样的编译器会抱怨f不存在。我们可以看到f在基类中,但编译器不会在那里查找它。

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

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

上面的代码是否正确?

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()不依赖于任何模板参数,因此编译器会在第一阶段查找期间尝试解析它。此时,模板尚未使用该类型实例化,编译器不会查看基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 编译器中的已知不符合项,它不实现两阶段查找。它将模板内的所有查找延迟到第二阶段,此时查找成功。

相关内容

  • 没有找到相关文章

最新更新