Требование переопределения виртуальной функции для использования ключевого слова override
С++ 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
.
Ответы
Ответ 1
Похоже, что релиз GCC 5.1 добавил именно warning Я искал:
-Wsuggest-override
Предупреждение о переопределении виртуальных функций, которые не отмечены ключевым словом переопределения.
Компиляция с -Wsuggest-override
-Werror=suggest-override
затем обеспечит выполнение всеми переопределениями override
.
Ответ 2
Есть две вещи, которые вы можете сделать.
Во-первых, Clang 3.5 и выше имеют предупреждение -Winconsistent-missing-override
(вызванное -Wall
). Это не совсем подходит для вашего примера, но только если вы добавите void foo() override {}
в class B
, а не в class C
. Вы действительно хотите -Wmissing-override
, чтобы найти все отсутствующие override
, а не просто несогласованные. В настоящее время это не предусмотрено, но вы можете жаловаться на список рассылки Clang, и они могут добавить его.
Во-вторых, вы используете трюк Howard Hinnant, чтобы временно добавить final
в функцию члена базового класса и перекомпилировать. Затем компилятор найдет все последующие производные классы, которые пытаются переопределить функцию базового члена virtual
. Затем вы можете исправить недостающие. Это немного больше работы и требует частого перепроверки, когда расширяется иерархия классов.
Ответ 3
GCC и Clang покрываются другими ответами. Здесь же для VС++ из моего другого ответа:
Ниже приведены соответствующие номера предупреждений в VС++:
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
Чтобы включить эти два предупреждения, вы можете использовать одну из следующих опций:
- Установите уровень предупреждения на 4 в настройках проекта, а затем отключите предупреждения, которые вы не хотите. Это мой предпочтительный путь. Чтобы отключить нежелательные предупреждения уровня 4, перейдите к настройкам проектa > C/С++ > Дополнительно, а затем введите номера предупреждений в поле Отключить конкретные предупреждения.
-
Включить более двух предупреждений с помощью кода.
#pragma warning(default:4263)
#pragma warning(default:4266)
-
Включите два предупреждения в настройках проектa > C/С++ > Командная строка, а затем введите /w 34263/w34266. Здесь/wNxxxx опция означает включение предупреждений xxxx в Уровне N (N = 3 - уровень по умолчанию). Вы также можете сделать /wdNxxxx, который отключает предупреждение xxxx на уровне N.
Ответ 4
Проблема, которую я вижу с помощью -Werror=suggest-override
, заключается в том, что она не позволяет вам написать следующее:
void f() final {...}
Даже если здесь есть неявный override
. -Werror=suggest-override
не игнорирует это (как и должно быть, поскольку override
в этом случае избыточно)
Но это сложнее, чем это... Если вы пишете
virtual void f() final {...}
Это означает совершенно другую вещь, чем
virtual void f() override final {...}
В первом случае не нужно ничего переопределять! Второй делает.
Итак, я предполагаю, что проверка GCC реализована таким образом (например, иногда принимать избыточную override
), чтобы получить последний случай права. Но это плохо работает, например. с clang-tidy, который будет корректно удалять переопределение, когда окончание будет достаточным (но тогда компиляция GCC завершится неудачно...)