Function declaration
Объявление функции вводит имя функции и её тип. Определение функции связывает имя/тип функции с телом функции.
Содержание |
Объявление функции
Объявления функций могут появляться в любой области видимости. Объявление функции в области видимости класса вводит функцию-член класса (если не используется спецификатор friend ), подробнее см. member functions и friend functions .
noptr-declarator
(
parameter-list
)
cv
(необязательно)
ref
(необязательно)
except
(необязательно)
attr
(необязательно)
|
(1) | ||||||||
noptr-declarator
(
parameter-list
)
cv
(необязательно)
ref
(необязательно)
except
(необязательно)
attr
(необязательно)
->
trailing
|
(2) | (начиная с C++11) | |||||||
(см. Declarations для других форм синтаксиса declarator )
| noptr-declarator | - |
любой допустимый
declarator
, но если он начинается с
*
,
&
, или
&&
, он должен быть заключен в круглые скобки.
|
||||||
| parameter-list | - | возможно пустой, разделенный запятыми список параметров функции (подробности см. ниже) | ||||||
| attr | - | (since C++11) список attributes . Эти атрибуты применяются к типу функции, а не к самой функции. Атрибуты для функции появляются после идентификатора в деклараторе и объединяются с атрибутами, которые указаны в начале объявления, если таковые имеются. | ||||||
| cv | - | квалификация const/volatile, разрешена только в объявлениях нестатических функций-членов | ||||||
| ref | - | (since C++11) ref-квалификация, разрешена только в объявлениях нестатических функций-членов | ||||||
| except | - |
|
||||||
| trailing | - | Возвращаемый тип в конце, полезен если возвращаемый тип зависит от имен аргументов, как в template < class T, class U > auto add ( T t, U u ) - > decltype ( t + u ) ; или является сложным, как в auto fpif ( int ) - > int ( * ) ( int ) |
|
Как упоминалось в разделе Declarations , за декларатором может следовать requires clause , который объявляет связанные constraints для функции, которые должны быть удовлетворены для того, чтобы функция была выбрана в процессе overload resolution . (пример: void f1 ( int a ) requires true ; ) Обратите внимание, что связанное ограничение является частью сигнатуры функции, но не частью типа функции. |
(since C++20) |
Деклараторы функций могут комбинироваться с другими деклараторами, когда это допускает последовательность спецификаторов объявления :
// объявляет int, int*, функцию и указатель на функцию int a = 1, *p = NULL, f(), (*pf)(double); // decl-specifier-seq это int // декларатор f() объявляет (но не определяет) // функцию без аргументов, возвращающую int struct S { virtual int f(char) const, g(int) &&; // объявляет две нестатические функции-члены virtual int f(char), x; // ошибка компиляции: virtual (в decl-specifier-seq) // разрешено только в объявлениях нестатических // функций-членов };
|
Использование типа объекта с квалификатором volatile в качестве типа параметра или возвращаемого типа устарело. |
(since C++20) |
Возвращаемый тип функции не может быть типом функции или типом массива (но может быть указателем или ссылкой на них).
|
Как и в любом объявлении, атрибуты, появляющиеся перед объявлением, и атрибуты, появляющиеся непосредственно после идентификатора внутри декларатора, применяются к объявляемой или определяемой сущности (в данном случае, к функции): [[noreturn]] void f [[noreturn]] (); // OK: both attributes apply to the function f Однако атрибуты, появляющиеся после декларатора (в приведённом выше синтаксисе), применяются к типу функции, а не к самой функции: void f() [[noreturn]]; // Error: this attribute has no effect on the function itself |
(начиная с C++11) |
Вывод типа возвращаемого значенияЕсли decl-specifier-seq объявления функции содержит ключевое слово auto , завершающий возвращаемый тип может быть опущен и будет выведен компилятором из типа операнда, используемого в неотброшенном return выражении. Если возвращаемый тип не использует decltype ( auto ) , вывод следует правилам template argument deduction : int x = 1; auto f() { return x; } // тип возвращаемого значения - int const auto& f() { return x; } // тип возвращаемого значения - const int&
Если возвращаемый тип -
decltype
(
auto
)
, то возвращаемый тип определяется так, как если бы операнд, используемый в операторе return, был обёрнут в
int x = 1; decltype(auto) f() { return x; } // тип возвращаемого значения - int, такой же как decltype(x) decltype(auto) f() { return(x); } // тип возвращаемого значения - int&, такой же как decltype((x)) (примечание: " const decltype ( auto ) & " является ошибкой, decltype ( auto ) должен использоваться самостоятельно) Если присутствует несколько операторов return, все они должны выводить один и тот же тип: auto f(bool val) { if (val) return 123; // выводит тип возвращаемого значения int else return 3.14f; // Ошибка: выводит тип возвращаемого значения float } Если отсутствует оператор return или если операнд оператора return является void-выражением (включая операторы return без операнда), объявленный возвращаемый тип должен быть либо decltype ( auto ) , в этом случае выведенный возвращаемый тип будет void , либо (возможно cv-квалифицированный) auto , в этом случае выведенный возвращаемый тип будет (идентично cv-квалифицированным) void : auto f() {} // возвращает void auto g() { return f(); } // возвращает void auto* x() {} // Ошибка: невозможно вывести auto* из void Как только оператор return был обнаружен в функции, тип возвращаемого значения, выведенный из этого оператора, может использоваться в остальной части функции, включая другие операторы return: auto sum(int i) { if (i == 1) return i; // возвращаемый тип sum - int else return sum(i - 1) + i; // OK: возвращаемый тип sum уже известен } Если оператор return использует список инициализации в фигурных скобках , вывод типа не допускается: auto func() { return {1, 2, 3}; } // Ошибка Виртуальные функции и корутины (since C++20) не могут использовать вывод типа возвращаемого значения: struct F { virtual auto f() { return 2; } // Ошибка }; Шаблоны функций , за исключением пользовательских функций преобразования , могут использовать вывод типа возвращаемого значения. Вывод происходит при инстанциации, даже если выражение в return statement не является зависимым . Данное инстанцирование не находится в непосредственном контексте для целей SFINAE . template<class T> auto f(T t) { return t; } typedef decltype(f(1)) fint_t; // инстанцирует f<int> для определения возвращаемого типа template<class T> auto f(T* t) { return *t; } void g() { int (*p)(int*) = &f; } // инстанцирует обе версии f для определения возвращаемых типов, // выбирает вторую перегрузку шаблона Переобъявления или специализации функций или шаблонов функций, использующие вывод типа возвращаемого значения, должны использовать те же заполнители для типа возвращаемого значения: auto f(int num) { return num; } // int f(int num); // Ошибка: нет типа-заполнителя возвращаемого значения // decltype(auto) f(int num); // Ошибка: другой заполнитель template<typename T> auto g(T t) { return t; } template auto g(int); // OK: тип возвращаемого значения - int // template char g(char); // Ошибка: не является специализацией основного шаблона g Аналогично, переобъявления или специализации функций или шаблонов функций, которые не используют вывод возвращаемого типа, не должны использовать заполнитель: int f(int num); // auto f(int num) { return num; } // Ошибка: не является переобъявлением f template<typename T> T g(T t) { return t; } template int g(int); // OK: специализация T как int // template auto g(char); // Ошибка: не является специализацией основного шаблона g Явные объявления инстанцирования сами по себе не инстанцируют шаблоны функций, использующие вывод типа возвращаемого значения: template<typename T> auto f(T t) { return t; } extern template auto f(int); // не инстанцирует f<int> int (*p)(int) = f; // инстанцирует f<int> для определения возвращаемого типа, // но явное определение инстанцирования // всё равно требуется где-то в программе |
(начиная с C++14) |
Список параметров
Список параметров определяет аргументы, которые могут быть указаны при вызове функции. Это разделённый запятыми список объявлений параметров , каждое из которых имеет следующий синтаксис:
| attr (необязательно) decl-specifier-seq declarator | (1) | ||||||||
|
attr
(необязательно)
|
(2) | (начиная с C++23) | |||||||
attr
(необязательно)
decl-specifier-seq
declarator
=
initializer
|
(3) | ||||||||
| attr (необязательно) decl-specifier-seq abstract-declarator (необязательно) | (4) | ||||||||
|
attr
(необязательно)
|
(5) | (начиная с C++23) | |||||||
attr
(необязательно)
decl-specifier-seq
abstract-declarator
(необязательно)
=
initializer
|
(6) | ||||||||
void
|
(7) | ||||||||
| Некорректное использование | Пример |
|---|---|
| присутствуют несколько параметров | int f1 ( void , int ) ; |
| параметр void имеет имя | inf f2 ( void param ) ; |
| void имеет cv-квалификаторы | int f3 ( const void ) ; |
| void является зависимым |
int
f4
(
T
)
;
(где
T
является
void
)
|
| параметр void является явным объектным параметром (начиная с C++23) | int f5 ( this void ) ; |
|
Хотя decl-specifier-seq подразумевает, что могут существовать спецификаторы помимо спецификаторов типа, единственным другим допустимым спецификатором является register а также auto (до C++11) , и он не имеет эффекта. |
(до C++17) |
|
Если любой из параметров функции использует заполнитель (либо auto , либо тип концепта ), то объявление функции является объявлением сокращенного шаблона функции : void f1(auto); // same as template<class T> void f1(T) void f2(C1 auto); // same as template<C1 T> void f2(T), if C1 is a concept |
(начиная с C++20) |
|
Объявление параметра со спецификатором this (синтаксис ( 2 ) / ( 5 ) ) объявляет явный объектный параметр . Явный объектный параметр не может быть пачкой параметров функции и может появляться только в качестве первого параметра в списке параметров в следующих объявлениях:
Функция-член с явным объектным параметром имеет следующие ограничения:
struct C { void f(this C& self); // OK template<typename Self> void g(this Self&& self); // also OK for templates void p(this C) const; // Error: “const” not allowed here static void q(this C); // Error: “static” not allowed here void r(int, this C); // Error: an explicit object parameter // can only be the first parameter }; // void func(this C& self); // Error: non-member functions cannot have // an explicit object parameter |
(начиная с C++23) |
Имена параметров, объявленные в объявлениях функций, обычно служат только для самодокументирования. Они используются (но остаются необязательными) в определениях функций.
Неоднозначность возникает в списке параметров, когда имя типа заключено в круглые скобки (включая лямбда-выражения ) (начиная с C++11) . В этом случае выбор стоит между объявлением параметра типа указатель на функцию и объявлением параметра с избыточными скобками вокруг идентификатора декларатора . Разрешение заключается в том, чтобы рассматривать имя типа как простой спецификатор типа (который является типом указателя на функцию):
class C {}; void f(int(C)) {} // void f(int(*fp)(C param)) {} // НЕ void f(int C) {} void g(int *(C[10])); // void g(int *(*fp)(C param[10])); // НЕ void g(int *C[10]);
Тип параметра не может быть типом, который включает ссылку или указатель на массив неизвестной границы, включая многоуровневые указатели/массивы таких типов, или указатель на функции, параметры которых являются такими типами.
Использование многоточия
Последний параметр в списке параметров может быть многоточием ( ... ); это объявляет вариативную функцию . Запятую перед многоточием можно опустить (устарело в C++26) :
int printf(const char* fmt, ...); // функция с переменным числом аргументов int printf(const char* fmt...); // то же самое, но устарело с C++26 template<typename... Args> void f(Args..., ...); // шаблон функции с переменным числом аргументов и пакетом параметров template<typename... Args> void f(Args... ...); // то же самое, но устарело с C++26 template<typename... Args> void f(Args......); // то же самое, но устарело с C++26
Тип функции
Список типов параметров
parameter-type-list функции определяется следующим образом:
- Тип каждого параметра (включая function parameter packs ) (since C++11) определяется из его собственного parameter declaration .
-
После определения типа каждого параметра, любой параметр типа "array of
T" или function typeTпреобразуется в "pointer toT". - После формирования списка типов параметров, любые top-level cv-qualifiers , модифицирующие тип параметра, удаляются при формировании function type.
- Полученный список преобразованных типов параметров и наличие или отсутствие ellipsis или function parameter pack (since C++11) является parameter-type-list функции.
void f(char*); // #1 void f(char[]) {} // определяет #1 void f(const char*) {} // OK, другая перегрузка void f(char* const) {} // Ошибка: переопределяет #1 void g(char(*)[2]); // #2 void g(char[3][2]) {} // определяет #2 void g(char[3][3]) {} // OK, другая перегрузка void h(int x(const int)); // #3 void h(int (*)(int)) {} // определяет #3
Определение типа функции
В синтаксисе
(1)
, предполагая
noptr-declarator
как самостоятельное объявление, при условии что тип
qualified-id
или
unqualified-id
в
noptr-declarator
равен «derived-declarator-type-list
T
»:
|
(начиная с C++17) |
-
Тип
(до C++17)
В противном случае тип
(начиная с C++17)
объявленной функции является
«список-производных-деклараторов-типов функция от
списка-типов-параметров cv (опционально) ref (опционально) (начиная с C++11) возвращающаяT».
|
В синтаксисе
(2)
, предполагая
noptr-declarator
как самостоятельное объявление, при условии, что тип
qualified-id
или
unqualified-id
в
noptr-declarator
равен "derived-declarator-type-list
|
(since C++11) |
|
(since C++17) |
attr , если присутствует, применяется к типу функции. |
(since C++11) |
// тип "f1" — // "функция, принимающая int и возвращающая void, с атрибутом noreturn" void f1(int a) [[noreturn]]; // тип "f2" — // "constexpr noexcept функция, принимающая указатель на int и возвращающая int" constexpr auto f2(int[] b) noexcept -> int; struct X { // тип "f3" — // "функция без параметров, константная, возвращающая const int" const int f3() const; };
Квалификаторы в конце объявления
Тип функции с
cv
или
ref
(начиная с C++11)
(включая тип, именованный через
typedef
) может появляться только как:
- тип функции для нестатической функции-члена ,
- тип функции, на которую ссылается указатель на член,
- тип функции верхнего уровня в объявлении typedef или объявлении псевдонима (начиная с C++11) ,
- type-id в аргументе по умолчанию шаблонного параметра типа , или
- type-id аргумента шаблона для шаблонного параметра типа.
typedef int FIC(int) const; FIC f; // Ошибка: не объявляет функцию-член struct S { FIC f; // OK }; FIC S::*pm = &S::f; // OK
Сигнатура функции
Каждая функция имеет сигнатуру.
Сигнатура функции состоит из её имени и parameter-type-list . Её сигнатура также включает охватывающее namespace , за следующими исключениями:
- Если функция является member function , её сигнатура содержит класс, членом которого является функция, вместо включающего пространства имён. Сигнатура также содержит следующие компоненты, если они существуют:
-
- cv
|
(начиная с C++11) |
|
(начиная с C++20) |
except and attr (since C++11) не затрагивает сигнатуру функции , хотя noexcept спецификация влияет на тип функции (since C++17) .
Определение функции
Определение функции, не являющейся членом, может появляться только в области видимости пространства имен (вложенные функции отсутствуют). Определение функции-члена также может появляться в теле определения класса . Они имеют следующий синтаксис:
|
attr
(необязательно)
decl-specifier-seq
(необязательно)
declarator
virt-specs (необязательно) contract-specs (необязательно) function-body |
(1) | ||||||||
|
attr
(необязательно)
decl-specifier-seq
(необязательно)
declarator
requires-clause contract-specs (необязательно) function-body |
(2) | (since C++20) | |||||||
| attr | - | (since C++11) список атрибутов . Эти атрибуты объединяются с атрибутами после идентификатора в деклараторе (см. начало страницы), если таковые имеются. |
| decl-specifier-seq | - | тип возвращаемого значения со спецификаторами, как в грамматике объявлений |
| declarator | - | функциональный декларатор, такой же как в грамматике объявления функций выше (может быть заключен в скобки) |
| virt-specs | - |
(since C++11)
override
,
final
, или их комбинация в любом порядке
|
| requires-clause | - | requires условие |
| contract-specs | - | (since C++26) список спецификаторов контракта функции |
| function-body | - | тело функции (см. ниже) |
function-body
может быть одним из следующих:
| ctor-initializer (необязательно) compound-statement | (1) | ||||||||
| function-try-block | (2) | ||||||||
=
default
;
|
(3) | (начиная с C++11) | |||||||
=
delete
;
|
(4) | (начиная с C++11) | |||||||
=
delete
(
string-literal
);
|
(5) | (начиная с C++26) | |||||||
| ctor-initializer | - | список инициализации членов , разрешён только в конструкторах |
| compound-statement | - | заключённая в фигурные скобки последовательность операторов , составляющая тело функции |
| function-try-block | - | блок function try block |
| string-literal | - | невычисляемый строковый литерал , который может использоваться для объяснения причины удаления функции |
int max(int a, int b, int c) { int m = (a > b) ? a : b; return (m > c) ? m : c; } // decl-specifier-seq — это «int» // declarator — это «max(int a, int b, int c)» // body — это { ... }
Тело функции представляет собой составной оператор (последовательность из нуля или более операторов, заключенных в пару фигурных скобок), который выполняется при вызове функции. Кроме того, тело функции конструктора также включает следующее:
- Для всех нестатических членов данных, идентификаторы которых отсутствуют в списке инициализации членов конструктора, используются инициализаторы членов по умолчанию или (начиная с C++11) инициализации по умолчанию для инициализации соответствующих подобъектов членов.
- Для всех базовых классов, имена типов которых отсутствуют в списке инициализации членов конструктора, используются инициализации по умолчанию для инициализации соответствующих подобъектов базовых классов.
|
Если определение функции содержит virt-specs , оно должно определять функцию-член . |
(начиная с C++11) |
|
Если определение функции содержит requires-clause , оно должно определять шаблонную функцию . |
(начиная с C++20) |
void f() override {} // Ошибка: не функция-член void g() requires (sizeof(int) == 4) {} // Ошибка: не шаблонная функция
Типы параметров, а также возвращаемый тип определения функции не могут быть (возможно cv-квалифицированными) неполными классами если только функция не определена как удалённая (since C++11) . Проверка полноты осуществляется только в теле функции, что позволяет функциям-членам возвращать класс, в котором они определены (или его внешний класс), даже если он неполон в точке определения (в теле функции он является полным).
Параметры, объявленные в деклараторе определения функции, находятся в области видимости внутри тела функции. Если параметр не используется в теле функции, его не нужно именовать (достаточно использовать абстрактный декларатор):
void print(int a, int) // второй параметр не используется { std::printf("a = %d\n", a); }
Несмотря на то, что топ-уровневые cv-квалификаторы параметров отбрасываются в объявлениях функций, они модифицируют тип параметра, видимый в теле функции:
void f(const int n) // объявляет функцию типа void(int) { // но в теле тип "n" является const int }
Функции по умолчаниюЕсли определение функции имеет синтаксис ( 3 ) , функция определяется как явно стандартная . Функция, которая явно объявлена как default, должна быть специальной функцией-членом или функцией оператора сравнения (начиная с C++20) , и она не должна иметь аргументов по умолчанию .
Явно определенная по умолчанию специальная функция-член
Если тип
Функция, явно заданная по умолчанию при первом объявлении, неявно является inline и неявно constexpr, если она может быть constexpr function . struct S { S(int a = 0) = default; // ошибка: аргумент по умолчанию void operator=(const S&) = default; // ошибка: несоответствующий тип возвращаемого значения ~S() noexcept(false) = default; // OK, другая спецификация исключений private: int i; S(S&); // OK, приватный конструктор копирования }; S::S(S&) = default; // OK, определяет конструктор копирования Явно-определенные по умолчанию функции и неявно объявленные функции вместе называются defaulted функциями. Их фактические определения будут предоставлены неявно, подробности смотрите на соответствующих страницах. Удалённые функцииЕсли определение функции имеет синтаксис ( 4 ) или ( 5 ) (начиная с C++26) , функция определяется как явно удалённая . Любое использование удалённой функции является некорректным (программа не скомпилируется). Это включает вызовы — как явные (с оператором вызова функции), так и неявные (вызов удалённого перегруженного оператора, специальной функции-члена, функции выделения памяти и т.д.), создание указателя или указателя на член на удалённую функцию, и даже использование удалённой функции в выражении, которое не является потенциально вычисляемым . Нечистая виртуальная функция-член может быть определена как удалённая (deleted), даже если она неявно odr-used . Удалённая функция может быть переопределена только удалёнными функциями, а неудалённая функция может быть переопределена только неудалёнными функциями.
Если функция перегружена, разрешение перегрузки происходит сначала, и программа является некорректной только если удалённая функция была выбрана: struct T { void* operator new(std::size_t) = delete; void* operator new[](std::size_t) = delete("new[] is deleted"); // since C++26 }; T* p = new T; // Ошибка: попытка вызова удалённого T::operator new T* p = new T[5]; // Ошибка: попытка вызова удалённого T::operator new[], // выводит диагностическое сообщение "new[] is deleted" Удалённое определение функции должно быть первым объявлением в единице трансляции: ранее объявленная функция не может быть переобъявлена как удалённая: struct T { T(); }; T::T() = delete; // Ошибка: должно быть удалено при первом объявлении Пользовательские функцииФункция является предоставленной пользователем если она объявлена пользователем и не является явно defaulted или deleted при первом объявлении. Предоставленная пользователем явно defaulted функция (т.е. явно defaulted после первого объявления) определяется в точке, где она явно defaulted; если такая функция неявно определяется как deleted, программа является некорректной. Объявление функции как defaulted после первого объявления может обеспечить эффективное выполнение и краткое определение, одновременно позволяя стабильный бинарный интерфейс для развивающейся кодовой базы. // Все специальные функции-члены «trivial» // определены по умолчанию в своих первых объявлениях соответственно, // они не являются предоставленными пользователем struct trivial { trivial() = default; trivial(const trivial&) = default; trivial(trivial&&) = default; trivial& operator=(const trivial&) = default; trivial& operator=(trivial&&) = default; ~trivial() = default; }; struct nontrivial { nontrivial(); // первое объявление }; // не определен по умолчанию при первом объявлении, // предоставлен пользователем и определен здесь nontrivial::nontrivial() = default; Разрешение Неоднозначностей
В случае неоднозначности между телом функции и
инициализатором
, начинающимся с
using T = void(); // тип функции using U = int; // не функциональный тип T a{}; // определяет функцию, не выполняющую действий U b{}; // инициализирует значение объекта int T c = delete("hello"); // определяет функцию как удаленную U d = delete("hello"); // копирует инициализацию объекта int с // результатом выражения delete (некорректно)
__func__В теле функции предопределенная локальная переменная __func__ определяется так, как если бы static const char __func__[] = "имя-функции"; Эта переменная имеет блочную область видимости и статическую продолжительность хранения: struct S { S(): s(__func__) {} // OK: список инициализации является частью тела функции const char* s; }; void f(const char* s = __func__); // Ошибка: список параметров является частью декларатора
Запустить этот код
Возможный вывод: Foo Bar Pub ~Bar |
(начиная с C++11) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Спецификаторы контракта функцииОбъявления функций и лямбда-выражения могут содержать последовательность спецификаторов контракта функции , каждый спецификатор имеет следующий синтаксис:
1)
Вводит
утверждение предусловия
.
2,3)
Вводит
постусловие утверждения
.
2)
Утверждение не связывается с результатом.
3)
Утверждение связывается с результатом.
Утверждение контракта функции — это утверждение контракта , связанное с функцией. Предикат утверждения контракта функции представляет собой его предикат контекстно преобразованный в bool . Следующие функции не могут быть объявлены с спецификаторами контракта функции:
Утверждения предусловийУтверждение предусловия связано с входом в функцию: int divide(int dividend, int divisor) pre(divisor != 0) { return dividend / divisor; } double square_root(double num) pre(num >= 0) { return std::sqrt(num); } Постусловия утвержденийПостусловие ассерта связано с нормальным выходом из функции. Если постусловие содержит identifier , спецификатор контракта функции вводит identifier как имя result binding для связанной функции. Result binding обозначает объект или ссылку, возвращаемые вызовом этой функции. Тип result binding соответствует возвращаемому типу связанной функции. int absolute_value(int num) post(r : r >= 0) { return std::abs(num); } double sine(double num) post(r : r >= -1.0 && r <= 1.0) { if (std::isnan(num) || std::isinf(num)) // выход через исключение никогда не вызывает нарушение контракта throw std::invalid_argument("Неверный аргумент"); return std::sin(num); } Если постусловие содержит идентификатор , а возвращаемый тип связанной функции является (возможно, cv-квалифицированным) void , программа является некорректно сформированной: void f() post(r : r > 0); // Ошибка: для "r" не может быть связано значение Когда объявленный возвращаемый тип нетemplated функции содержит тип-заполнитель , постусловие с идентификатором может появляться только в определении функции: auto g(auto&) post(r : r >= 0); // OK, "g" является шаблоном auto h() post(r : r >= 0); // Ошибка: невозможно указать имя возвращаемого значения auto k() post(r : r >= 0) // OK, "k" является определением { return 0; } Согласованность контрактов
Переобъявление
redeclaration
Если объявление
Два contract-specs одинаковы, если они состоят из одинаковых спецификаторов контрактов функций в одинаковом порядке.
Спецификатор контракта функции
Если это условие не выполняется исключительно из-за сравнения двух лямбда-выражений, которые содержатся в predicate s, диагностика не требуется. bool b1, b2; void f() pre (b1) pre([]{ return b2; }()); void f(); // OK, спецификаторы контракта функции опущены void f() pre (b1) pre([]{ return b2; }()); // Ошибка: замыкания имеют разные типы void f() pre (b1); // Ошибка: спецификаторы контракта функции различаются int g() post(r : b1); int g() post(b1); // Ошибка: отсутствует привязка результата namespace N { void h() pre (b1); bool b1; void h() pre (b1); // Ошибка: спецификаторы контракта функции различаются // согласно правилу одного определения } |
(начиная с C++26) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Примечания
В случае неоднозначности между объявлением переменной с использованием синтаксиса прямой инициализации и объявлением функции, компилятор всегда выбирает объявление функции; см. direct-initialization .
| Макрос тестирования возможностей | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_decltype_auto
|
201304L
|
(C++14) |
decltype(auto)
|
__cpp_return_type_deduction
|
201304L
|
(C++14) | выведение возвращаемого типа для обычных функций |
__cpp_explicit_this_parameter
|
202110L
|
(C++23) | явные параметры объекта ( выведение this ) |
__cpp_deleted_function
|
202403L
|
(C++26) | удалённая функция с причиной |
Ключевые слова
Пример
#include <iostream> #include <string> // простая функция с аргументом по умолчанию, не возвращающая значение void f0(const std::string& arg = "world!") { std::cout << "Hello, " << arg << '\n'; } // объявление находится в области видимости пространства имен (файла) // (определение предоставлено позже) int f1(); // функция, возвращающая указатель на f0, в стиле до C++11 void (*fp03())(const std::string&) { return f0; } // функция, возвращающая указатель на f0, с завершающим типом возврата C++11 auto fp11() -> void(*)(const std::string&) { return f0; } int main() { f0(); fp03()("test!"); fp11()("again!"); int f2(std::string) noexcept; // объявление в области видимости функции std::cout << "f2(\"bad\"): " << f2("bad") << '\n'; std::cout << "f2(\"42\"): " << f2("42") << '\n'; } // простая нечленная функция, возвращающая int int f1() { return 007; } // функция с спецификацией исключений и блоком try функции int f2(std::string str) noexcept try { return std::stoi(str); } catch (const std::exception& e) { std::cerr << "stoi() failed!\n"; return 0; } // удаленная функция, попытка вызова приводит к ошибке компиляции void bar() = delete # if __cpp_deleted_function ("reason") # endif ;
Возможный вывод:
stoi() failed!
Hello, world!
Hello, test!
Hello, again!
f2("bad"): 0
f2("42"): 42
Отчёты о дефектах
Следующие отчеты об изменениях в поведении, содержащие описания дефектов, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение в опубликованной версии | Корректное поведение |
|---|---|---|---|
| CWG 135 | C++98 |
функции-члены, определенные в классе,
не могли иметь параметр или возвращать собственный класс, так как он неполный |
разрешено |
| CWG 332 | C++98 | параметр мог иметь cv-квалифицированный void тип | запрещено |
| CWG 393 | C++98 |
типы, включающие указатели/ссылки на
массив неизвестной границы, не могли быть параметрами |
такие типы разрешены |
| CWG 452 | C++98 | список инициализации членов не был частью тела функции | является частью |
| CWG 577 | C++98 |
зависимый тип
void
мог использоваться для
объявления функции без параметров |
разрешен только независимый
void |
| CWG 1327 | C++11 |
функции по умолчанию или удаленные не могли
быть указаны с override или final |
разрешено |
| CWG 1355 | C++11 | только специальные функции-члены могли быть предоставлены пользователем | расширено на все функции |
| CWG 1394 | C++11 |
удаленные функции не могли иметь параметр
неполного типа или возвращать неполный тип |
неполный тип разрешен |
| CWG 1824 | C++98 |
проверка полноты типа параметра и
возвращаемого типа определения функции могла выполняться вне контекста определения функции |
проверка только в
контексте определения функции |
| CWG 1877 | C++14 | вывод возвращаемого типа трактовал return ; как return void ( ) ; |
просто выводит возвращаемый
тип как void в этом случае |
| CWG 2015 | C++11 |
неявное odr-использование удаленной
виртуальной функции было некорректным |
такие odr-использования освобождены
от запрета использования |
| CWG 2044 | C++14 |
вывод возвращаемого типа для функций, возвращающих
void
завершался неудачей, если объявленный возвращаемый тип - decltype ( auto ) |
обновлено правило вывода
для обработки этого случая |
| CWG 2081 | C++14 |
переобъявления функций могли использовать вывод
возвращаемого типа, даже если первоначальное объявление не использует |
не разрешено |
| CWG 2144 | C++11 | { } могло быть телом функции или инициализатором в одном месте |
различается по типу
идентификатора декларатора |
| CWG 2145 | C++98 | декларатор в определении функции не мог быть заключен в скобки | разрешено |
| CWG 2259 | C++11 |
правило разрешения неоднозначности относительно заключенных в скобки
имен типов не охватывало лямбда-выражения |
охвачено |
| CWG 2430 | C++98 |
в определении функции-члена в определении класса
тип этого класса не мог быть возвращаемым типом или типом параметра из-за решения CWG issue 1824 |
проверка только в
теле функции |
| CWG 2760 | C++98 |
тело функции конструктора не включало инициализации
не указанные в обычном теле функции конструктора |
также включает эти
инициализации |
| CWG 2831 | C++20 |
определение функции с
requires-предложением
могло определять нешаблонную функцию |
запрещено |
| CWG 2846 | C++23 | функции-члены с явным объектом не могли иметь определений вне класса | разрешено |
| CWG 2915 | C++23 | неименованные параметры явного объекта могли иметь тип void | запрещено |
Смотрите также
|
C documentation
для
Declaring functions
|