【问题标题】:Deriving implementation of pure virtual function推导纯虚函数的实现
【发布时间】:2013-12-31 03:38:15
【问题描述】:

考虑下面的例子

#include <iostream>

struct PureVirtual {
    virtual void Function() = 0;
};

struct FunctionImpl {
    virtual void Function() {
        std::cout << "FunctionImpl::Function()" << std::endl;
    }   
};

struct NonPureVirtual : public FunctionImpl, public PureVirtual {
    using FunctionImpl::Function;
};

int main() {
    NonPureVirtual c;
    c.Function();
}

编译器(GCC 4.9,Clang 3.5)退出并出现错误

test.cpp:18:20: error: variable type 'NonPureVirtual' is an abstract class
    NonPureVirtual c;
                   ^
test.cpp:4:18: note: unimplemented pure virtual method 'Function' in 'NonPureVirtual'
    virtual void Function() = 0;
                 ^

但是当我没有从PureVirtual 派生出来时,一切都很好。这很奇怪,因为标准 10.4.4

一个类是抽象的,如果它包含或继承至少一个纯 最终覆盖器是纯虚拟的虚函数。

他们并没有说明 final overrider 是什么,但我想它应该是 FunctionImpl::Function(),尤其是当我通过 using 指令使其可用时。那么为什么仍然是 NonPureVirtual 抽象类,我该如何解决这个问题。

【问题讨论】:

  • PureVirtual 不是FunctionImpl 的基础,所以...
  • public FunctionImpl, public PureVirtual -> Function 在两个类中都是可见的,特别是从 PureVirtual 中可见,没有虚拟继承,因此它使其成为一个抽象类。一个猜测。
  • @Simple - 我不这么认为,或者至少 - 可能版本的 gcc 中存在错误。我遇到过同样的情况,我解决了它,但我在这里没有答案。
  • @Constructor - 正是如此。问题是为什么NonPureVirtual 是抽象的。
  • PureVirtual 继承时它无法编译的原因是你只影响FunctionImpl::Functionusing 的范围/可访问性;你没有覆盖任何东西。

标签: c++ polymorphism abstract-class pure-virtual virtual-functions


【解决方案1】:

FunctionImpl::FunctionPureVirtual::Function 是不同类的不同函数。

它们各自的类型是void (FunctionImpl::*)()void (PureVirtual::*)()
由于PureVirtualFunctionImpl 是不相关的类,因此这些函数类型是不相关的。

它们恰好具有相同的名称、相同的参数和返回类型,但由于它们不同,using FunctionImpl::Function 行不会使该函数覆盖PureVirtual 中的函数。

如果您声明了一个void (PureVirtual::*)() 类型的变量,您将无法将FunctionImpl::Function 分配给它。

换句话说,PureVirtual::Function 的最终覆盖是 PureVirtual 中的原始覆盖,它是纯虚拟的。

为了使您想要的成为可能,Matthieu M. 的回答(使用转接电话)似乎是要走的路。

【讨论】:

  • 是的,这解释了情况,我的解释是一样的,但是“如何解决它?”。您可以在上面看到我的评论:“您可以在 NonPureVirtual 中执行 /* virtual */ void Function() { FunctionImpl::Function(); } 但我不知道这是否定义明确。很可能 - 不是。”
  • 例如,我尝试的是void PureVirtual::Function() { FunctionImpl::Function(); } ,但正如预期的那样,这不是一个有效的语法。
  • @KirilKirov 我怀疑它可能无法修复,但我不确定。重命名可能是最好的方法。
  • 是的,当我遇到这个问题时,这就是我“解决”这个问题的方式。但我还是有希望的,有更好的办法:)
  • @Kiril struct NonPureVirtual : ... { void Function() { } }; ... c.FunctionImpl::Function(); 怎么样
【解决方案2】:

您不能使用using 指令,而必须求助于转发调用:

struct NonPureVirtual : public FunctionImpl, public PureVirtual {
    virtual void Function() override {
        return FunctionImpl::Function();
    }
};

是的,it works as expected

【讨论】:

  • 我不认为override 改变了任何东西,仍然不清楚它覆盖了哪个基本函数,或者我错了?对于这种情况,我不会依赖测试。
  • 我不明白您在@suspectus 已删除答案下的评论。我的意思是 - 你的答案如何解决嫌疑人的歧义问题?
  • @KirilKirov: override 没有任何改变,在 C++11 中标记为 override 函数实际上是覆盖的好形式;有或没有overrideNonPureVirtual::Function 只是覆盖 both FunctionImpl::FunctionPureVirtual::Function。关于歧义:@suspectus 的回答中没有没有歧义,但是它使用复制/粘贴来重现为维护而应避免的行为。
  • +1,我不知道你可以“同时”覆盖两个函数。
【解决方案3】:

您将 using 声明归因于它不做的事情。它的作用是(来自草案 n3337, 7.3.3/1):

... using-declaration 将名称引入到 using-declaration 出现的声明区域中。

同一段落的后面:

如果一个 using-declaration 命名一个构造函数(3.4.3.1),它在 using-declaration 出现的类 (12.9); 否则在 using 声明中指定的名称是 在别处声明的某个实体名称的同义词。

在您的情况下,FunctionImpl::Function 首先在 FunctionImpl 类中声明,它是它的成员。使用声明不会改变这一事实,正如第 16 段所说的那样:

出于重载决议的目的,由 using-declaration 引入派生类的函数将被视为派生类的成员。特别是,隐式this 参数应被视为指向派生类而不是基类的指针。 这对函数的类型没有影响,并且在所有其他方面,该函数仍然是 基类。

现在开始定义覆盖 (10.3/2):

如果一个虚成员函数vf在类Base和类Derived中声明,直接或间接派生自Base,则成员函数vf同名,参数类型-list (8.3.5)、cv-qualifi cation 和 ref-qualifier(或不存在相同)作为 Base::vf 被声明,然后 Derived::vf 也是虚拟的(无论是否如此声明)并且它 覆盖 Base::vf。为方便起见,我们说任何虚函数都会覆盖自身。

在同一段落中还定义了最终覆盖器。

类对象S 的虚拟成员函数C::vf最终覆盖器,除非其中S 是基类子对象(如果有)的最派生类(1.8)声明或继承另一个覆盖vf 的成员函数。在派生类中,如果基类子对象的虚成员函数具有多个最终覆盖器,则程序是非良构的。 [...]

我认为这清楚地表明您不能使用 using 声明来覆盖虚函数。 Matthieu's answer 中有一个可能的修复方法。

【讨论】:

    猜你喜欢
    • 2016-03-07
    • 2016-02-18
    • 1970-01-01
    • 2011-01-06
    • 2021-10-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多