这有一个完整的新手问题的感觉,但为什么在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()
不会覆盖其基类中的虚函数?我怎样才能实现这一点( - 不更改C::operator()
的名称?)
编辑:正如几个用户所指出的,答案很简单,派生类中的virtual
- 关键字是多余的(而我认为将其删除会阻止继承) 。但是,我在讨论这个问题时的目标 - 即整个动态和静态继承层次结构中的一致接口 - 可以通过使用非虚拟operator[]
遍历和耦合类来解决{虚拟函数A
{1}}和B
:
apply
答案 0 :(得分:6)
如果函数在基类中声明为virtual
,则使用相同名称和参数列表声明的函数在派生类中隐式virtual
,无论您是否使用virtual
}关键字。您无法使C::operator()()
非虚拟。
答案 1 :(得分:4)
具有与基类中的虚函数相同的签名的派生类中的函数将覆盖来自基类的虚函数。这使得它成为一个虚函数,即使/虽然派生类中的声明不使用virtual
关键字。
无法更改,所以如果确实需要在派生类中具有相同名称的函数,该函数不会覆盖基类中的虚函数(并且在进程,变为虚拟本身,在这种情况下,违反final
中的B
,您需要更改派生类中函数的签名。这可能意味着不同的名称,不同的参数列表或不同的限定符。我会用极端谨慎对待后两者 - 编译器将能够解决你所做的混乱,但许多人类读者可能(很容易)感到惊讶。
如果我正在审核此类代码,我可能会将此视为一个问题,并且作者需要提供非常可靠的推理,以确定为什么它真正必要才能获得批准。
答案 2 :(得分:1)
作为覆盖(因为它与基类中的虚函数具有相同的签名),覆盖与其基类中指定的final
冲突。
一个修复(或更确切地说是解决方法)是为该函数提供一个默认参数,以便它具有不同的类型,因此不是覆盖,更好的方法是修复设计。