Спецификатор final (начиная с C++11) — cppreference.com
Материал из cppreference.com
Указывает, что виртуальная функция не может быть переопределена в производном классе или что класс не может наследоваться.
Синтаксис
При применении к функции-элементу идентификатор final появляется сразу после декларатора в синтаксисе объявления функции-элемента или определения функции-элемента внутри определения класса.
Применительно к классу идентификатор final появляется в начале определения класса, сразу после имени класса.
| декларатор последовательность-спецификаторов-виртуальности (необязательно) чистый-спецификатор (необязательно) | (1) | ||||||||
| декларатор последовательность-спецификаторов-виртуальности (необязательно) тело-функции | (2) | ||||||||
| ключевое-слово-класса атрибуты (необязательно) имя-заголовка-класса спецификатор-виртуальности-класса (необязательно) предложение-базы (необязательно) | (3) | ||||||||
1) В объявлении функции-элемента final может появиться в последовательности-спецификаторов-виртуальности сразу после декларатора и перед чистым-спецификатором, если используется.
2) В определении функции-элемента внутри определения класса final может появиться в последовательности-спецификаторов-виртуальности сразу после декларатора и непосредственно перед телом-функции.
3) В определении класса final может отображаться как спецификатор-виртуальности-класса сразу после имени класса, прямо перед двоеточием, с которого начинается предложение-базы, если используется.
В случаях (1,2) последовательность-спецификаторов-виртуальности, если она используется, равна либо override, либо final, либо final override или override final. В случае (3) единственным допустимым значением спецификатора-виртуальности-класса, если он используется, является final.
Объяснение
При использовании в объявлении или определении виртуальной функции спецификатор final гарантирует, что функция является виртуальной, и указывает, что она не может быть переопределена производными классами. В противном случае программа некорректна (генерируется ошибка времени компиляции).
При использовании в определении класса final указывает, что этот класс не может появляться в списке-базовых-спецификаторов другого определения класса (другими словами, не может наследоваться). В противном случае программа некорректна (генерируется ошибка времени компиляции). final также можно использовать с определением union, и в этом случае оно не будет иметь никакого эффекта (кроме результата std::is_final) (начиная с C++14), поскольку объединения не могут наследоваться.
final это идентификатор со специальным значением при использовании в объявлении функции-элемента или заголовке класса. В других контекстах он не зарезервирован и может использоваться для обозначения объектов и функций.
Примечание
В последовательности следующих токенов:
- один из
class,structиunion; - возможно квалифицированный идентификатор;
final;- один из
:и{,
третий токен final в последовательности всегда считается спецификатором, а не идентификатором:
struct A; struct A final {}; // ОК, определение структуры A, а не // инициализация значения переменной final struct X { struct C { constexpr operator int() { return 5; } }; struct B final : C{}; // ОК, определение вложенного класса B, // а не объявление элемента битового поля final }; // Ненормальное использование final. struct final final // ОК, определение структуры с именем `final`, { // от которой вы не можете наследовать }; // struct final final {}; // Ошибка: переопределение `struct final`, а НЕ // определение переменной `final` с использованием // сложного спецификатора типа `struct final`, // за которым следует агрегатная инициализация // struct override : final {}; // Ошибка: не может быть выведен базового типа final; // `override` в данном контексте является обычным именем void foo() { [[maybe_unused]] final final; // ОК, объявление переменной с именем `final` // типа `struct final` } struct final final; // ОК, объявление переменной с именем `final` типа // `struct final` с использованием сложного спецификатора типа int main() { }
Пример
struct Base { virtual void foo(); }; struct A : Base { void foo() final; // Base::foo переопределяется, а A::foo // является окончательным переопределением void bar() final; // Ошибка: bar не может быть окончательной, // так как она не виртуальная }; struct B final : A // структура B является окончательной { void foo() override; // Ошибка: foo не может быть переопределена, // так как она является окончательной в A }; struct C : B {}; // Ошибка: B является окончательным
Возможный вывод:
main.cpp:9:10: error: 'void A::bar()' marked 'final', but is not virtual
9 | void bar() final; // Ошибка: не может быть окончательной,
// так как она не виртуальная
| ^~~
main.cpp:14:10: error: virtual function 'virtual void B::foo()' overriding final function
14 | void foo() override; // Ошибка: foo не может быть переопределена,
// так как она является окончательной в A
| ^~~
main.cpp:8:10: note: overridden function is 'virtual void A::foo()'
8 | void foo() final; // Base::foo переопределяется, а A::foo
// является окончательным переопределением
| ^~~
main.cpp:17:8: error: cannot derive from 'final' base 'B' in derived type 'C'
17 | struct C : B // Ошибка: B является окончательным
|
Ссылки
- C++23 стандарт (ISO/IEC 14882:2023):
- 11 Классы [class]
- 11.7.3 Виртуальные функции [class.virtual]
- C++20 стандарт (ISO/IEC 14882:2020):
- 11 Классы [class]
- 11.7.2 Виртуальные функции [class.virtual]
- C++17 стандарт (ISO/IEC 14882:2017):
- 12 Классы [class]
- 13.3 Виртуальные функции [class.virtual]
- C++14 стандарт (ISO/IEC 14882:2014):
- 9 Классы [class]
- 10.3 Виртуальные функции [class.virtual]
- C++11 стандарт (ISO/IEC 14882:2011):
- 9 Классы [class]
- 10.3 Виртуальные функции [class.virtual]
Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
| Номер | Применён | Поведение в стандарте | Корректное поведение |
|---|---|---|---|
| CWG 1318 | C++11 | определение класса, которое имеет final после имени классаи пустой список спецификаций элементов, может сделать finalидентификатором |
final в этом случае всегда являетсяспецификатором |