Спецификатор final (начиная с C++11)
Указывает, что виртуальная функция не может быть переопределена в производном классе или что класс не может наследоваться.
Синтаксис
При применении к функции-элементу идентификатор final появляется сразу после декларатора в синтаксисе объявления функции-элемента или определения функции-элемента внутри определения класса.
Применительно к классу идентификатор final появляется в начале определения класса, сразу после имени класса.
| декларатор последовательность-спецификаторов-виртуальности (необязательно) чистый-спецификатор (необязательно) | (1) | ||||||||
| декларатор последовательность-спецификаторов-виртуальности (необязательно) тело-функции | (2) | ||||||||
| ключевое-слово-класса атрибуты (необязательно) имя-заголовка-класса спецификатор-виртуальности-класса (необязательно) предложение-базы (необязательно) | (3) | ||||||||
final может появиться в последовательности-спецификаторов-виртуальности сразу после декларатора и перед чистым-спецификатором, если используется.final может появиться в последовательности-спецификаторов-виртуальности сразу после декларатора и непосредственно перед телом-функции.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 в этом случае всегда являетсяспецификатором |
Смотрите также
спецификатор override(C++11)
|
явно объявляет, что метод переопределяет другой метод |