派生 class 中非虚函数的 C++ 同名与 `final` 说明符冲突

C++ same name for non-virtual function in derived class conflicts with `final` specifier

感觉完全是菜鸟问题,但是为什么final说明符用于B::operator()时,下面的代码编译不通过?

struct A
{
    virtual void operator()() const = 0;
};

// the CRTP-component is not really necessary here
// but it possibly makes more sense in that it could be applied like this in reality
//
template<typename Derived>
struct B : A
{
    virtual void operator()() const override final
    {
        static_cast<Derived const&>(*this).operator()();
    }
};

struct C : B<C>
{
    void operator()() const
    {
        //do something
    }
};

int main()
{
    C()();
}

G++ 打印以下内容 error message:

main.cpp:17:14: error: virtual function 'virtual void C::operator()() const'
         void operator()() const
              ^
main.cpp:9:22: error: overriding final function 'void B<Derived>::operator()() const [with Derived = C]'
         virtual void operator()() const override final
                      ^

我原以为它可以工作,因为非虚拟 C::operator() 不会覆盖其基础 classes 中的虚拟函数?我怎样才能使它起作用(--不更改 C::operator() 的名称)?


编辑:正如一些用户所指出的,答案很简单,派生的 class 中的 virtual-关键字是多余的(而我认为将其排除在外会阻止继承)。然而,我提出这个问题的目的——即贯穿动态和静态继承层次结构的一致接口——可以通过在整个和通过虚函数 apply:

耦合 classes AB
struct A
{
    void operator()() const
    {
        this->apply();
    }

protected:
    virtual void apply() const = 0;
};

template<typename Derived>
struct B : A
{
    void operator()() const
    {
        static_cast<Derived const&>(*this).operator()();
    }

protected:
    virtual void apply() const override final
    {
        this->operator()();
    }
};

struct C : B<C>
{
    void operator()() const
    {
        //do something
    }
};

int main()
{
    C()();
}

如果一个函数在基础 class 中声明为 virtual,则在派生 class 中隐式地 virtual 使用相同名称和参数列表声明的函数,无论您是否使用 virtual 关键字。您不能使 C::operator()() 非虚拟。

作为覆盖(因为它与基 class 中的虚函数具有相同的签名),覆盖与其基 class 中指定的 final 冲突。

一个解决方法(或者更确切地说是解决方法)是为该函数提供一个默认参数,使其具有不同的类型,因此不是覆盖,更好的方法是修复设计。

派生 class 中与基 class 中的虚函数具有相同签名的函数覆盖基 class 中的虚函数。这使它成为一个虚函数,即使 if/though 派生的 class 中的声明也不使用 virtual 关键字。

无法更改,因此如果您 确实 需要在派生的 class 中有一个同名的函数,它不会覆盖虚拟来自基础 class 的函数(在这个过程中,本身变成虚拟的,在这种情况下,违反了 B 中的 final)你需要改变函数的签名派生class。这可能意味着不同的名称、不同的参数列表或不同的限定符。尽管我会 极端 谨慎对待后两者——编译器将能够解决您造成的混乱,但许多人类读者可能(很容易)感到惊讶。

如果我正在审查这样的代码,我可能会认为这是一个问题,作者需要提供非常可靠的推理来说明为什么 有必要 得到它批准了。