Объявление функции
Объявление функции вводит имя функции и её тип. Определение функции связывает имя/тип функции с телом функции.
Объявление функции
Объявления функций могут встречаться в любой области видимости. Объявление функции в области видимости класса вводит функцию-член класса (если не используется спецификатор friend); подробности см. в статьях Функции-члены и Функции friend.
noptr-declarator ( parameter-list ) cv (необязательно) ref (необязательно) except (необязательно) attr (необязательно) | (1) | |
noptr-declarator ( parameter-list ) cv (необязательно) ref (необязательно) except (необязательно) attr (необязательно)-> trailing | (2) | (начиная с C++11) |
(другие формы синтаксиса declarator см. в статье Объявления)
| noptr-declarator | - | любая допустимая конструкция declarator, но если она начинается с *, & или &&, её необходимо заключить в круглые скобки. |
||||||
| parameter-list | - | возможно, пустой список параметров функции, разделённых запятыми (подробности см. ниже) | ||||||
| attr | - | (начиная с C++11) список атрибутов. Эти атрибуты применяются к типу функции, а не к самой функции. Атрибуты функции указываются после идентификатора в деклараторах и объединяются с атрибутами, если таковые имеются, указанными в начале объявления. | ||||||
| cv | - | квалификация const/volatile, разрешённая только в объявлениях нестатических функций-членов | ||||||
| ref | - | (начиная с C++11) ref-квалификация, разрешённая только в объявлениях нестатических функций-членов | ||||||
| except | - |
|
||||||
| trailing | - | Завершающий тип возврата, полезный, если тип возврата зависит от имён аргументов, например template<class T, class U> auto add(T t, U u) -> decltype(t + u);, или имеет сложную форму, например в auto fpif(int)->int(*)(int) |
| Как упоминалось в статье Объявления, за деклараторами может следовать предложение requires, объявляющее связанные с функцией ограничения, которые должны выполняться, чтобы функция могла быть выбрана при разрешении перегрузки. (пример: | (начиная с C++20) |
Деклараторы функций можно сочетать с другими деклараторами, если это допускает последовательность спецификаторов объявления:
// declares an int, an int*, a function, and a pointer to a function
int a = 1, *p = NULL, f(), (*pf)(double);
// decl-specifier-seq is int
// declarator f() declares (but doesn't define)
// a function taking no arguments and returning int
struct S
{
virtual int f(char) const, g(int) &&; // declares two non-static member functions
virtual int f(char), x; // compile-time error: virtual (in decl-specifier-seq)
// is only allowed in declarations of non-static
// member functions
};
| Использование квалифицированного volatile объектного типа в качестве типа параметра или типа возврата объявлено устаревшим. | (начиная с 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), выведение выполняется по правилам выведения аргументов шаблона: int x = 1;
auto f() { return x; } // return type is int
const auto& f() { return x; } // return type is const int&
Если тип возврата — decltype(auto), тип возврата совпадает с типом, который был бы получен, если бы операнд, используемый в операторе return, был заключён в int x = 1;
decltype(auto) f() { return x; } // return type is int, same as decltype(x)
decltype(auto) f() { return(x); } // return type is int&, same as decltype((x))
(примечание: «constdecltype(auto)&» — ошибка; decltype(auto) необходимо использовать отдельно) Если операторов return несколько, все они должны выводить один и тот же тип: auto f(bool val)
{
if (val) return 123; // deduces return type int
else return 3.14f; // Error: deduces return type float
}
Если оператор return отсутствует или его операнд представляет собой выражение типа void, объявленный тип возврата должен быть либо decltype(auto), и тогда выведенный тип возврата — void, либо auto (возможно, с cv-квалификаторами), и тогда выведенный тип возврата — void с теми же cv-квалификаторами: auto f() {} // returns void
auto g() { return f(); } // returns void
auto* x() {} // Error: cannot deduce auto* from void
После того как в функции встречен оператор return, выведенный из него тип возврата можно использовать в оставшейся части функции, в том числе в других операторах return: auto sum(int i)
{
if (i == 1)
return i; // sum’s return type is int
else
return sum(i - 1) + i; // OK: sum’s return type is already known
}
Если в операторе return используется список инициализации в фигурных скобках, выведение не допускается: auto func() { return {1, 2, 3}; } // Error
Виртуальные функции и сопрограммы(начиная с C++20) не могут использовать выведение типа возврата: struct F
{
virtual auto f() { return 2; } // Error
};
Шаблоны функций, за исключением пользовательских функций преобразования, могут использовать выведение типа возврата. Выведение выполняется при инстанцировании, даже если выражение в операторе return не является зависимым. Для целей SFINAE это инстанцирование не является непосредственным контекстом. template<class T>
auto f(T t) { return t; }
typedef decltype(f(1)) fint_t; // instantiates f<int> to deduce return type
template<class T>
auto f(T* t) { return *t; }
void g() { int (*p)(int*) = &f; } // instantiates both fs to determine return types,
// chooses second template overload
Повторные объявления или специализации функций либо шаблонов функций, в которых используется выведение типа возврата, должны использовать те же заполнители типа возврата: auto f(int num) { return num; }
// int f(int num); // Error: no placeholder return type
// decltype(auto) f(int num); // Error: different placeholder
template<typename T>
auto g(T t) { return t; }
template auto g(int); // OK: return type is int
// template char g(char); // Error: not a specialization of the primary template g
Аналогично, повторные объявления или специализации функций либо шаблонов функций, в которых не используется выведение типа возврата, не должны использовать заполнитель: int f(int num);
// auto f(int num) { return num; } // Error: not a redeclaration of f
template<typename T>
T g(T t) { return t; }
template int g(int); // OK: specialize T as int
// template auto g(char); // Error: not a specialization of the primary template g
Объявления явного инстанцирования сами по себе не инстанцируют шаблоны функций, в которых используется выведение типа возврата: template<typename T>
auto f(T t) { return t; }
extern template auto f(int); // does not instantiate f<int>
int (*p)(int) = f; // instantiates f<int> to determine its return type,
// but an explicit instantiation definition
// is still required somewhere in the program
| (начиная с 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 f(void); и int f(); объявляют одну и ту же функцию.void — единственный синтаксис, эквивалентный пустому списку параметров; другие варианты использования параметров void некорректны: | Некорректное использование | Пример |
|---|---|
| указано несколько параметров |
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). В этом случае возможны два варианта: объявление параметра типа «указатель на функцию» или объявление параметра с избыточными скобками вокруг идентификатора declarator. Для разрешения неоднозначности имя типа считается простым спецификатором типа (то есть типом «указатель на функцию»):
class C {};
void f(int(C)) {} // void f(int(*fp)(C param)) {}
// NOT void f(int C) {}
void g(int *(C[10])); // void g(int *(*fp)(C param[10]));
// NOT void g(int *C[10]);
Тип нельзя определять в объявлении параметра функции или в типе возврата:
void f(struct S1 {} s); // Error
struct S2 {} g(); // Error
Тип параметра не может включать ссылку или указатель на массив неизвестной границы, в том числе многоуровневые указатели/массивы таких типов, а также указатель на функции, параметры которых имеют такие типы.
Использование многоточия
Последним параметром в списке параметров может быть многоточие (...); это объявляет вариативную функцию. Запятую перед многоточием можно опустить(устарело в C++26):
int printf(const char* fmt, ...); // a variadic function int printf(const char* fmt...); // same as above, but deprecated since C++26 template<typename... Args> void f(Args..., ...); // a variadic function template with a parameter pack template<typename... Args> void f(Args... ...); // same as above, but deprecated since C++26 template<typename... Args> void f(Args......); // same as above, but deprecated since C++26
Тип функции
Тип функции образуется с учётом наличия или отсутствия noexcept (присутствует тогда и только тогда, когда except не допускает исключений), (начиная с C++17)ref, (начиная с C++11)cv, списка типов параметров (см. ниже) и типа возврата.
Список типов параметров
Список типов параметров функции определяется следующим образом:
- Тип каждого параметра (включая пакеты параметров функции)(начиная с C++11) определяется его собственным объявлением параметра.
- После определения типа каждого параметра любой параметр типа «массив из
T» или типа функцииTпреобразуется в «указатель наT». - После формирования списка типов параметров все cv-квалификаторы верхнего уровня, изменяющие тип параметра, удаляются при формировании типа функции.
- Полученный список преобразованных типов параметров и наличие или отсутствие многоточия или пакета параметров функции(начиная с C++11) и есть список типов параметров функции.
void f(char*); // #1
void f(char[]) {} // defines #1
void f(const char*) {} // OK, another overload
void f(char* const) {} // Error: redefines #1
void g(char(*)[2]); // #2
void g(char[3][2]) {} // defines #2
void g(char[3][3]) {} // OK, another overload
void h(int x(const int)); // #3
void h(int (*)(int)) {} // defines #3
Завершающие квалификаторы
Тип функции с cv или ref (начиная с C++11) (в том числе тип, заданный именем typedef) может встречаться только как:
- тип функции для нестатической функции-члена,
- тип функции, на которую указывает указатель на член,
- тип функции верхнего уровня в объявлении typedef функции или в объявлении псевдонима(начиная с C++11),
- type-id в аргументе по умолчанию параметра типа шаблона,
- type-id аргумента шаблона для параметра типа шаблона,
| (начиная с C++26) |
typedef int FIC(int) const;
FIC f; // Error: does not declare a member function
struct S
{
FIC f; // OK
};
FIC S::*pm = &S::f; // OK
constexpr auto yeti = ^^void(int) const&; // OK (since C++26)
Сигнатура функции
У каждой функции есть сигнатура.
Сигнатура функции состоит из её имени и списка типов параметров. В сигнатуру также входит охватывающее пространство имён, за следующими исключениями:
- Если функция является функцией-членом, её сигнатура содержит класс, членом которого является функция, вместо охватывающего пространства имён. Если существуют, в сигнатуру также входят следующие компоненты:
- cv
| (начиная с C++11) |
| (начиная с C++20) |
except и attr(начиная с C++11) не входят в сигнатуру функции, хотя спецификация noexcept влияет на тип функции(начиная с 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) | (начиная с C++20) |
| attr | - | (начиная с C++11) список атрибутов. Эти атрибуты объединяются с атрибутами, если таковые имеются, указанными после идентификатора в declarator (см. начало этой страницы). |
| decl-specifier-seq | - | тип возврата со спецификаторами, как в грамматике объявлений |
| declarator | - | декларатор функции, такой же, как в приведённой выше грамматике объявления функции (может быть заключён в скобки) |
| virt-specs | - |
(начиная с C++11) override, final или их сочетание в любом порядке |
| requires-clause | - | предложение requires |
| contract-specs | - | (начиная с 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 | - | блок try функции |
| string-literal | - | невычисляемый строковый литерал, который можно использовать для пояснения причины удаления функции |
int max(int a, int b, int c)
{
int m = (a > b) ? a : b;
return (m > c) ? m : c;
}
// decl-specifier-seq is “int”
// declarator is “max(int a, int b, int c)”
// body is { ... }
Тело функции представляет собой составной оператор (последовательность из нуля или более операторов, заключённую в пару фигурных скобок), которая выполняется при вызове функции. Кроме того, тело функции конструктора также включает следующее:
- Для всех нестатических элементов данных, идентификаторы которых отсутствуют в списке инициализаторов членов конструктора, используются инициализаторы членов по умолчанию или(начиная с C++11) инициализация по умолчанию для инициализации соответствующих подобъектов-членов.
- Для всех базовых классов, имена типов которых отсутствуют в списке инициализаторов членов конструктора, используется инициализация по умолчанию для инициализации соответствующих подобъектов базового класса.
| Если определение функции содержит virt-specs, оно должно определять функцию-член. | (начиная с C++11) |
| Если определение функции содержит requires-clause, оно должно определять шаблонную функцию. | (начиная с C++20) |
void f() override {} // Error: not a member function
void g() requires (sizeof(int) == 4) {} // Error: not a templated function
Типы параметров, а также возвращаемый тип определения функции не могут быть (возможно, cv-квалифицированными) неполными типами классов, если только функция не определена как удалённая(начиная с C++11). Проверка полноты выполняется только в теле функции, что позволяет функциям-членам возвращать класс, в котором они определены (или его охватывающий класс), даже если он неполон в точке определения (в теле функции он является полным).
Параметры, объявленные в declarator определения функции, видны в теле. Если параметр не используется в теле функции, ему не обязательно задавать имя (достаточно использовать абстрактный декларатор):
void print(int a, int) // second parameter is not used
{
std::printf("a = %d\n", a);
}
Хотя квалификаторы cv верхнего уровня у параметров отбрасываются в объявлениях функций, они изменяют тип параметра, видимый в теле функции:
void f(const int n) // declares function of type void(int)
{
// but in the body, the type of “n” is const int
}
Функции, заданные по умолчаниюЕсли определение функции имеет синтаксис (3), функция определяется как явно заданная по умолчанию. Функция, явно заданная по умолчанию, должна быть специальной функцией-членом или функцией-оператором сравнения(начиная с C++20) и не должна иметь аргументов по умолчанию. Явно заданная по умолчанию специальная функция-член
Если тип
Функция, явно заданная по умолчанию в своём первом объявлении, неявно является встроенной и неявно является constexpr, если она может быть функцией constexpr. struct S
{
S(int a = 0) = default; // error: default argument
void operator=(const S&) = default; // error: non-matching return type
~S() noexcept(false) = default; // OK, different exception specification
private:
int i;
S(S&); // OK, private copy constructor
};
S::S(S&) = default; // OK, defines copy constructor
Явно заданные по умолчанию функции и неявно объявленные функции в совокупности называются функциями, заданными по умолчанию. Их определения предоставляются неявно; подробности см. на соответствующих страницах. Удалённые функцииЕсли определение функции имеет синтаксис (4) или (5)(начиная с C++26), функция определяется как явно удалённая. Любое использование удалённой функции , кроме использования в качестве операнда оператора отражения,(начиная с C++26) некорректно (программа не будет скомпилирована). Это включает явные вызовы (с помощью оператора вызова функции) и неявные вызовы (вызов удалённого перегруженного оператора, специальной функции-члена, функции выделения памяти и т. д.), создание указателя или указателя на член для удалённой функции и даже использование удалённой функции в выражении, которое не является потенциально вычисляемым. Нечисто виртуальную функцию-член можно определить как удалённую, даже если она неявно используется в смысле ODR. Удалённую функцию могут переопределять только удалённые функции, а не удалённую функцию — только не удалённые функции.
Если функция перегружена, сначала выполняется разрешение перегрузки, и программа некорректна только в том случае, если была выбрана удалённая функция: 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; // Error: attempts to call deleted T::operator new
T* p = new T[5]; // Error: attempts to call deleted T::operator new[],
// emits a diagnostic message “new[] is deleted”
Удалённое определение функции должно быть первым объявлением в единице трансляции: ранее объявленную функцию нельзя повторно объявить удалённой: struct T { T(); };
T::T() = delete; // Error: must be deleted on the first declaration
Функции, предоставленные пользователемФункция является предоставленной пользователем, если она объявлена пользователем и не задана явно по умолчанию и не удалена в своём первом объявлении. Функция, предоставленная пользователем и явно заданная по умолчанию (то есть явно заданная по умолчанию после первого объявления), определяется в точке, где она явно задана по умолчанию; если такая функция неявно определяется как удалённая, программа некорректна. Объявление функции заданной по умолчанию после её первого объявления позволяет добиться эффективного выполнения и лаконичного определения, сохраняя при этом стабильный двоичный интерфейс развивающейся кодовой базы. // All special member functions of “trivial” are
// defaulted on their first declarations respectively,
// they are not user-provided
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(); // first declaration
};
// not defaulted on the first declaration,
// it is user-provided and is defined here
nontrivial::nontrivial() = default;
Разрешение неоднозначностейВ случае неоднозначности между телом функции и инициализатором, начинающимся с
using T = void(); // function type
using U = int; // non-function type
T a{}; // defines a function doing nothing
U b{}; // value-initializes an int object
T c = delete("hello"); // defines a function as deleted
U d = delete("hello"); // copy-initializes an int object with
// the result of a delete expression (ill-formed)
__func__В теле функции предопределённая переменная, локальная для функции, static const char __func__[] = "function-name"; Эта переменная имеет область видимости блока и статическую длительность хранения: struct S
{
S(): s(__func__) {} // OK: initializer-list is part of function body
const char* s;
};
void f(const char* s = __func__); // Error: parameter-list is part of declarator
#include <iostream>
void Foo() { std::cout << __func__ << ' '; }
struct Bar
{
Bar() { std::cout << __func__ << ' '; }
~Bar() { std::cout << __func__ << ' '; }
struct Pub { Pub() { std::cout << __func__ << ' '; } };
};
int main()
{
Foo();
Bar bar;
Bar::Pub pub;
}
Возможный вывод: Foo Bar Pub ~Bar | (начиная с C++11) | |||||||||||||||||||||
Спецификаторы контрактов функцийОбъявления функций и лямбда-выражения могут содержать последовательность спецификаторов контрактов функций . Каждый спецификатор имеет следующий синтаксис:
Утверждение контракта функции — это утверждение контракта, связанное с функцией. Предикат утверждения контракта функции — это его predicate, контекстно преобразуемый к 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 как имя привязки результата связанной функции. Привязка результата обозначает объект или ссылку, возвращаемые при вызове этой функции. Тип привязки результата совпадает с возвращаемым типом связанной функции. 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))
// exiting via an exception never causes contract violation
throw std::invalid_argument("Invalid argument");
return std::sin(num);
}
Если утверждение постусловия содержит identifier , а возвращаемый тип связанной функции является (возможно, cv-квалифицированным) void, программа некорректна: void f() post(r : r > 0); // Error: no value can be bound to “r” Если объявленный возвращаемый тип нешаблонной функции содержит тип-заполнитель, утверждение постусловия с identifier может присутствовать только в определении функции: auto g(auto&) post(r : r >= 0); // OK, “g” is a template
auto h() post(r : r >= 0); // Error: cannot name the return value
auto k() post(r : r >= 0) // OK, “k” is a definition
{
return 0;
}
Согласованность контрактовПовторное объявление Если объявление Два набора contract-specs одинаковы, если они состоят из одних и тех же спецификаторов контрактов функций в одинаковом порядке. Спецификатор контракта функции
Если это условие нарушается только из-за сравнения двух лямбда-выражений, содержащихся в predicate , диагностика не требуется. bool b1, b2;
void f() pre (b1) pre([]{ return b2; }());
void f(); // OK, function contract specifiers omitted
void f() pre (b1) pre([]{ return b2; }()); // Error: closures have different types
void f() pre (b1); // Error: function contract specifiers are different
int g() post(r : b1);
int g() post(b1); // Error: no result binding
namespace N
{
void h() pre (b1);
bool b1;
void h() pre (b1); // Error: function contract specifiers differ
// according to the one−definition rule
}
| (начиная с C++26) |
Примечания
В случае неоднозначности между объявлением переменной с использованием синтаксиса прямой инициализации и объявлением функции компилятор всегда выбирает объявление функции; см. прямую инициализацию.
| Макрос проверки поддержки возможности | Значение | Стандарт | Возможность |
|---|---|---|---|
__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>
// simple function with a default argument, returning nothing
void f0(const std::string& arg = "world!")
{
std::cout << "Hello, " << arg << '\n';
}
// the declaration is in namespace (file) scope
// (the definition is provided later)
int f1();
// function returning a pointer to f0, pre-C++11 style
void (*fp03())(const std::string&)
{
return f0;
}
// function returning a pointer to f0, with C++11 trailing return type
auto fp11() -> void(*)(const std::string&)
{
return f0;
}
int main()
{
f0();
fp03()("test!");
fp11()("again!");
int f2(std::string) noexcept; // declaration in function scope
std::cout << "f2(\"bad\"): " << f2("bad") << '\n';
std::cout << "f2(\"42\"): " << f2("42") << '\n';
}
// simple non-member function returning int
int f1()
{
return 007;
}
// function with an exception specification and a function try block
int f2(std::string str) noexcept
try
{
return std::stoi(str);
}
catch (const std::exception& e)
{
std::cerr << "stoi() failed!\n";
return 0;
}
// deleted function, an attempt to call it results in a compilation error
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 | declarator в определении функции нельзя было заключать в скобки | разрешено |
| CWG 2259 | C++11 | правило разрешения неоднозначности для имён типов в скобках не охватывало лямбда-выражения | охватывает |
| CWG 2430 | C++98 | при определении функции-члена в определении класса тип этого класса не мог быть возвращаемым типом или типом параметра из-за решения проблемы CWG 1824 | проверка выполняется только в теле функции |
| CWG 2760 | C++98 | тело функции конструктора не включало инициализации, не указанные в обычном теле функции конструктора | также включает эти инициализации |
| CWG 2831 | C++20 | определение функции с requires-clause могло определять нешаблонную функцию | запрещено |
| CWG 2846 | C++23 | функции-члены с явным параметром объекта нельзя было определять вне класса | разрешено |
| CWG 2915 | C++23 | неименованные явные параметры объекта могли иметь тип void | запрещено |
См. также
| Документация по C о объявлении функций |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/function