【问题标题】:Why can I call base template class method from derived class为什么我可以从派生类调用基模板类方法
【发布时间】:2013-10-08 08:34:27
【问题描述】:

我决定测试“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
};

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

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

我们需要了解原因。 问题是当编译器遇到类模板B的定义时,他们 不知道它继承自哪个类。当然是A&lt;T&gt;,但是T是一个模板参数, 一个直到稍后才会知道(当B 被实例化时)。不知道是什么T 也就是说,没有办法知道A&lt;T&gt; 的类是什么样的。特别是,无法知道它是否具有f 函数。

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

上面的代码是否正确?

【问题讨论】:

  • f() 需要完全限定为基类名称或this-&gt;
  • @Troy "f() 需要完全合格.." 抱歉这是什么意思?
  • @Oleksiy 它适用于 MSVC,因为该编译器没有实现两阶段名称查找,因此不会在模板实例化之前尝试解析依赖名称。在实例化时,A&lt;T&gt;::f 不再是从属名称,因为 T 是已知的。而完全合格意味着如果你写A&lt;T&gt;::f(),它就会起作用。 C++-faq 解释了使用这种方法的缺陷。
  • @Praetorian:完全合格 言过其实。调用必须是依赖的,这可以通过添加this-&gt;qualifying 来实现。无需完全限定调用,只需要基本类型。

标签: c++ templates inheritance base


【解决方案1】:
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&lt;T&gt;。原因是编译器不可能知道对于实例化类型是否有可能不包含任何成员的 A&lt;T&gt; 特化。

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

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

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

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

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


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

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

【讨论】:

  • 另一种方式是using A&lt;T&gt;::f; 声明,或者我最喜欢的成语using B::A::f;
猜你喜欢
  • 2016-08-17
  • 2014-04-30
  • 1970-01-01
  • 1970-01-01
  • 2013-07-17
  • 2014-12-10
  • 2011-02-10
  • 2013-03-25
  • 2014-03-22
相关资源
最近更新 更多