Namespaces
Variants

final specifier (since C++11)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Virtual function
override specifier (C++11)
final specifier (C++11)
Special member functions
Templates
Miscellaneous

Указывает, что виртуальная функция не может быть переопределена в производном классе, или что класс не может быть унаследован .

Содержание

Синтаксис

При применении к функции-члену идентификатор final указывается непосредственно после декларатора в синтаксисе объявления функции-члена или определения функции-члена внутри определения класса.

При применении к классу (включая struct и union), идентификатор final указывается в начале определения класса, непосредственно после имени класса, и не может появляться в объявлении класса.

declarator virt-specifier-seq  (необязательно) pure-specifier  (необязательно) (1)
declarator virt-specifier-seq  (необязательно) function-body (2)
class-key attr  (необязательно) class-head-name class-virt-specifier  (необязательно) base-clause  (необязательно) (3) (до C++26)
class-key attr  (необязательно) class-head-name class-prop-specifier-seq  (необязательно) base-clause  (необязательно) (4) (начиная с C++26)
1) В объявлении функции-члена, final может появляться в virt-specifier-seq сразу после декларатора и перед pure-specifier , если он используется.
2) В определении функции-члена внутри определения класса, final может появляться в virt-specifier-seq сразу после декларатора и непосредственно перед function-body .
3) В определении класса final может использоваться как class-virt-specifier непосредственно после имени класса, непосредственно перед двоеточием, начинающим base-clause , если он используется.
4) В определении класса, final может появляться в class-prop-specifier-seq , если используется, но только один раз.

В случаях (1,2) , virt-specifier-seq , если используется, может быть либо override , либо final , либо final override , либо override final . В случае (3) единственным допустимым значением class-virt-specifier , если используется, является final . В случае (4) , class-prop-specifier-seq , если используется, может содержать любое количество спецификаторов свойств класса (начиная с C++26) , но каждый может встречаться не более одного раза.

Объяснение

При использовании в объявлении или определении виртуальной функции, final спецификатор гарантирует, что функция является виртуальной и указывает, что она не может быть переопределена производными классами. В противном случае программа является некорректной (генерируется ошибка времени компиляции).

При использовании в определении класса, final указывает, что этот класс не может появляться в base-specifier-list другого определения класса (другими словами, не может быть унаследован). В противном случае программа некорректна (генерируется ошибка компиляции). final также может использоваться с определением union , в этом случае он не имеет эффекта (кроме влияния на результат std::is_final ) (начиная с C++14) , поскольку объединения не могут быть унаследованы.

final — это идентификатор со специальным значением при использовании в объявлении функции-члена или заголовке класса. В других контекстах он не зарезервирован и может использоваться для именования объектов и функций.

Примечание

В последовательности следующих токенов:

  1. один из class , struct и union
  2. возможно квалифицированный идентификатор
  3. final
  4. один из : и {

третий токен final в последовательности всегда рассматривается как спецификатор, а не идентификатор.

struct A;
struct A final {}; // OK, определение структуры A,
                   // не инициализация переменной final
struct X
{
    struct C { constexpr operator int() { return 5; } };
    struct B final : C{}; // OK, определение вложенного класса B,
                          // не объявление битового поля final
};
// Нетипичное использование final.
struct final final // OK, определение структуры с именем `final`, от которой
{                  // нельзя наследоваться
};
// struct final final {}; // Ошибка: переопределение `struct final`, НЕ
                          // определение переменной `final` с использованием
                          // уточненного спецификатора типа `struct final`
                          // с последующей агрегатной инициализацией
// struct override : final {}; // Ошибка: нельзя наследовать от final базового типа;
                               // `override` в данном контексте является обычным именем
void foo()
{
    [[maybe_unused]]
    final final; // OK, объявление переменной с именем `final` типа
                 // `struct final` 
}
struct final final; // OK, объявление переменной с именем `final` типа
                    // `struct final` с использованием уточненного спецификатора типа
int main()
{
}

Ключевые слова

final

Пример

struct Base
{
    virtual void foo();
};
struct A : Base
{
    void foo() final; // Base::foo переопределена и A::foo является финальным переопределением
    void bar() final; // Ошибка: bar не может быть final, так как не является виртуальной
};
struct B final : A // struct B является final
{
    void foo() override; // Ошибка: foo не может быть переопределена, так как она final в A
};
struct C : B {}; // Ошибка: B является final

Возможный вывод:

main.cpp:9:10: error: 'void A::bar()' marked 'final', but is not virtual
    9 |     void bar() final; // Error: bar cannot be final as it is non-virtual
      |          ^~~
main.cpp:14:10: error: virtual function 'virtual void B::foo()' overriding final function
   14 |     void foo() override; // Error: foo cannot be overridden as it is final in A
      |          ^~~
main.cpp:8:10: note: overridden function is 'virtual void A::foo()'
    8 |     void foo() final; // Base::foo is overridden and A::foo is the final override
      |          ^~~
main.cpp:17:8: error: cannot derive from 'final' base 'B' in derived type 'C'
   17 | struct C : B // Error: B is final
      |

Ссылки

  • Стандарт C++23 (ISO/IEC 14882:2024):
  • 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++.

DR Применяется к Поведение как опубликовано Корректное поведение
CWG 1318 C++11 определение класса, которое имеет final после имени класса и пустой
список спецификаций членов может сделать final идентификатором
final всегда является
спецификатором в этом случае

Смотрите также

override спецификатор (C++11) явно объявляет, что метод переопределяет другой метод
спецификаторы свойств класса (C++26) final спецификатор (C++11) , заменяемость (C++26) , тривиальная перемещаемость (C++26)