要求使用override关键字重写虚函数

52

C++11增加了override关键字,以确保您编写的成员函数实际上覆盖基类的虚拟函数(否则编译不会通过)。

但是在大型对象层次结构中,有时候您可能会意外地编写一个成员函数来覆盖基类的虚拟函数,而这并不是您的本意!例如:

struct A {
    virtual void foo() { }  // because obviously every class has foo().
};

struct B : A { ... };

class C : B {
private:
    void foo() {
        // was intended to be a private function local to C
        // not intended to override A::foo(), but now does
    }
};

是否有一些编译器标志或扩展可以至少在C::foo上发出警告?为了可读性和正确性,我只想强制所有重写使用override

是否有一些编译器标志或扩展可以至少在C::foo上发出警告?为了可读性和正确性,我只想强制所有重写使用override


实际上,您的函数将被命名为GazimpleWidget(Widget& w),显然C::GazimpleWidget(Widget& w)仍然会简化小部件。只有在尝试将C::GazimpleWidget()缩写为C::GW()时才会遇到此类问题。不要这样做。 - MSalters
请参考:https://dev59.com/5GYs5IYBdhLWcg3wAfEi - Anton Savin
@MSalters 我不理解这个评论。 - Barry
@Barry:你简化的例子存在问题,因为 foo 是误导性的。真正有意义的函数名很少会无意中发生冲突。如果它们被命名相同,则应该执行相同的操作,然后覆盖可能不是错误。 - MSalters
11
“不太可能”,“应该是”,“很可能不...”。 是的,显然。整个问题的重点在于不太可能发生的情况已经发生了,函数实现有误,我希望在编译时验证这一点。 - Barry
4个回答

47

看起来GCC 5.1版本添加了我正在寻找的确切警告

-Wsuggest-override
       当覆盖未标记为override关键字的虚函数时发出警告。

使用-Wsuggest-override-Werror=suggest-override进行编译,会强制要求所有的覆盖都使用override


他们在GCC 10.3及更高版本的文档中删除了此选项。但该选项仍然适用于这些版本。 - VainMan
1
@VainMan 一半的警告在Dialect Options,原因不明,非常令人困惑。 - Barry

14

你可以采取两种方法。

首先,Clang 3.5及更高版本有一个 -Winconsistent-missing-override 报警(由 -Wall 触发)。这在你的示例中并不完全适用,而是只有当你在 class B 中添加了 void foo() override {} 而不是在 class C 中时才适用。你实际想要的是 -Wmissing-override,以定位所有缺失的 override,而不仅仅是不一致地缺失。目前还没有提供,但你可以在 Clang 邮件列表上抱怨,他们可能会添加它。

其次,你使用 Howard Hinnant 的技巧 来临时添加 final 到基类成员函数并重新编译。编译器将会定位尝试重写该虚拟基类成员函数的所有进一步派生类。然后你可以修复遗漏的部分。这需要更多的工作,并需要在类层次结构扩展时进行频繁的重新检查。


9
我认为使用-Werror=suggest-override的问题在于它不允许你编写以下内容:
void f() final {...}

尽管这里有一个隐式的override,但是-Werror=suggest-override并不会忽略它(正如它应该做的那样,因为在这种情况下override是多余的)。
但实际情况比这更加复杂...如果你这样写:
virtual void f() final {...}

这意味着它和之前提到的东西完全不同。
virtual void f() override final {...}

第一个案例不需要覆盖任何东西!第二个需要。
因此,我假设GCC检查是这样实现的(即有时接受冗余的override),以便正确处理最后一种情况。但这与clang-tidy不兼容,当使用final足够时,clang-tidy将正确地删除override(但然后GCC编译会失败...)。

1
我同意你的观点。然而,如果我们遵循只在首次引入新虚函数时使用“virtual”关键字的建议,那么你所展示的最终函数可能会覆盖或不覆盖的问题就不存在了。(这禁止了将虚函数引入并将其实现为final的代码,这似乎是毫无意义的。)然后含义就很清楚了:“virtual”=新的非覆盖虚函数,“override”或“final”都表示它们是覆盖(且没有“virtual”),两者都应满足g++警告(但不幸的是并没有)。 - Chris Uzdavinis
2
在同一行中声明和完成一个基本的虚拟函数,从而使其成为虚拟函数的意义丧失,似乎完全没有意义。我甚至会说语言应该禁止这样做,那么编译器就不必考虑它的哲学思想了。 - underscore_d
我同意@underscore_d和Scott Meyers的观点。如果只有一种实现f()的方法,请坚定立场并将其声明为非虚拟的。 - Robert Skinner

0
GCC和Clang已经在其他答案中提到了。这里是关于VC++的同样内容,可以参考我的其他答案
以下是VC++中相关的警告编号:
C4263 (level 4) 'function': member function does not override any base class virtual member function
C4266 (level 4) 'function': no override available for virtual member function from base 'type'; function is hidden

为了启用这两个警告,您可以使用以下选项之一:

  1. 在项目设置中将警告级别设置为4,然后禁用您不想要的警告。这是我首选的方法。要禁用不需要的4级警告,请转到项目设置> C/C++>高级,然后在“禁用特定警告”框中输入警告号码。
  2. 使用代码启用上述两个警告。

    #pragma warning(default:4263)
    #pragma warning(default:4266)
    
  3. 在项目设置> C/C++>命令行中启用上述两个警告,然后输入/w34263 /w34266。这里/wNxxxx选项表示在N级(N = 3是默认级别)中启用xxxx警告。您还可以使用/wdNxxxx来禁用级别N中的xxxx警告。


3
问题是如何在覆盖方法时未使用“override”关键字时启用编译器警告,而不是如何在错误使用“override”关键字时启用警告。 - Marco Freudenberger
注意:我不建议将这些内容添加到项目设置的命令行部分。有一个专门用于警告的部分;将它们放在那里更有意义。 - celticminstrel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接