Объявление функции
Объявление функции вводит имя функции и ее тип. Определение функции связывает имя/тип функции с телом функции.
Объявление функции
Объявления функций могут появляться в любом области видимости. Объявление функции в области класса вводит функцию-член класса (если не используется спецификатор friend), см. функции-члены и дружественные функции для получения подробной информации.
noptr-declarator ( parameter-list ) cv (необязательно) ref (необязательно) except (необязательно) attr (необязательно) | (1) | |
noptr-declarator ( parameter-list ) cv (необязательно) ref (необязательно) except (необязательно) attr (необязательно)-> trailing | (2) | (с C++11) |
(см. Объявления для других форм синтаксиса declarator)
auto. | noptr-declarator | - | любой допустимый declarator, но если он начинается с *, &, или &&, он должен быть заключен в скобки. |
||||||||
| parameter-list | - | может быть пустым, список параметров функции, разделенных запятыми (см. подробности ниже) | ||||||||
| attr | - | (с C++11) список атрибутов. Эти атрибуты применяются к типу функции, а не к самой функции. Атрибуты для функции появляются после идентификатора в declarator и объединяются с атрибутами, которые появляются в начале объявления, если таковые имеются. | ||||||||
| 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) |
| Как упоминалось в Объявлениях, после declarator может следовать requires-оператор, который объявляет связанные ограничения для функции, которые должны быть выполнены, чтобы функция была выбрана разрешением перегрузки. (пример: | (с C++20) |
Определения функций могут сочетаться с другими определениями, где decl-specifier-seq допускает:
// 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) |
Тип возвращаемого значения функции не может быть типом функции или типом массива (но может быть указателем или ссылкой на эти типы).
| Как и в любом объявлении, атрибуты, которые появляются перед объявлением и атрибуты, которые появляются сразу после идентификатора в declarator, оба применяются к объявляемому или определяемому объекту (в данном случае к функции): [[noreturn]] void f [[noreturn]] (); // okay: both attributes apply to the function f Однако атрибуты, которые появляются после declarator (в синтаксисе выше), применяются к типу функции, а не к самой функции: 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))(примечание: "const decltype(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 отсутствует или если аргумент оператора return - это выражение void, объявленный тип возвращаемого значения должен быть либо decltype(auto), в этом случае выведенный тип возвращаемого значения - void, или (возможно, с квалификатором cv) auto, в этом случае выведенный тип возвращаемого значения тогда (тождественно с квалификатором cv) void: 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; // okay: sum’s return type is already known
}Если оператор return использует braced-init-list, вывод не разрешен: 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); // okay: 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); // okay: 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 (необязательно) decl-specifier-seq declarator = initializer | (2) | |
| attr (необязательно) decl-specifier-seq abstract-declarator (необязательно) | (3) | |
attr (необязательно) decl-specifier-seq abstract-declarator (необязательно) = initializer | (4) | |
void | (5) |
int f(int a, int* p, int (*(*x)(double))[3]);
int f(int a = 7, int* p = nullptr, int (*(*x)(double))[3] = nullptr);
int f(int, int*, int (*(*)(double))[3]);
int f(int = 7, int* = nullptr, int (*(*)(double))[3] = nullptr);
int f(void); и int f(); объявляют одну и ту же функцию. Обратите внимание, что тип void (возможно, с квалификаторами cv) не может использоваться в списке параметров иначе: int f(void, int); и int f(const void); являются ошибками (хотя производные типы, такие как void* могут быть использованы). В шаблоне может использоваться только независимый тип void (функция, принимающая один параметр типа T не становится функцией без параметров, если она инстанцирована с T = void)Эллипс ... может появляться в конце списка параметров; это объявляет функцию с переменным числом аргументов:
int printf(const char* fmt ...);
Для совместимости с C89, необязательная запятая может появляться перед эллипсом, если список параметров содержит по крайней мере один параметр:
int printf(const char* fmt, ...); // OK, same as above
| Хотя decl-specifier-seq подразумевает, что могут существовать спецификаторы помимо спецификаторов типов, единственный другой допустимый спецификатор — register а также | (до 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) |
Имена параметров, объявленные в объявлениях функций, обычно служат только для самодокументирования. Они используются (но остаются необязательными) в определениях функций.
Тип каждого параметра функции в списке параметров определяется по следующим правилам:
int f(const int p, decltype(p)*); и int f(int, const int*); объявляют одну и ту же функцию).Из-за этих правил следующие объявления функций объявляют одну и ту же функцию:
int f(char s[3]); int f(char[]); int f(char* s); int f(char* const); int f(char* volatile s);
Следующие объявления также объявляют одну и ту же функцию:
int f(int()); int f(int (*g)());
Возникает неоднозначность в списке параметров, когда имя типа вложенно в скобки (включая лямбда-выражения)(начиная с 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]);Тип параметра не может быть типом, который включает ссылку или указатель на массив неизвестной длины, включая многоуровневые указатели/массивы таких типов, или указатель на функции, параметры которых являются такими типами.
| Эллипс, указывающий на переменное число аргументов, не должен предваряться запятой, даже если он следует за эллипсом, указывающим на расширение пакета параметров, поэтому следующие шаблоны функций являются идентичными: template<typename... Args> void f(Args..., ...); template<typename... Args> void f(Args... ...); template<typename... Args> void f(Args......); Пример использования такого объявления — возможное реализация #include <cstdio>
template<typename... Variadic, typename... Args>
constexpr void invoke(auto (*fun)(Variadic......), Args... args)
{
fun(args...);
}
int main()
{
invoke(std::printf, "%dm•%dm•%dm = %d%s%c", 2,3,7, 2*3*7, "m³", '\n');
}Вывод: 2m•3m•7m = 42m³ | (начиная с C++11) |
Тип функции
Список типов параметров
Список типов параметров функции определяется следующим образом:
- Тип каждого параметра (включая пакеты параметров функций)(начиная с 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Определение типа функции
В синтаксисе (1), предполагая noptr-declarator как самостоятельное объявление, учитывая тип qualified-id или unqualified-id в noptr-declarator как «список типов производных деклараторов T»:
| (начиная с C++17) |
- В(до C++17)В противном случае, (начиная с C++17) тип объявленной функции —
«список типов производных деклараторов функция с
списком типов параметров cv (необязательно) ref (необязательно)(начиная с C++11) возвращающаяT».
| В синтаксисе (2), предполагая noptr-declarator как самостоятельное объявление, учитывая тип qualified-id или unqualified-id в noptr-declarator как «список типов производного декларатора
| (с C++11) |
| attr, если присутствует, применяется к типу функции. | (с C++11) |
// the type of “f1” is
// “function of int returning void, with attribute noreturn”
void f1(int a) [[noreturn]];
// the type of “f2” is
// “constexpr noexcept function of pointer to int returning int”
constexpr auto f2(int[] b) noexcept -> int;
struct X
{
// the type of “f3” is
// “function of no parameter const returning const int”
const int f3() const;
};Последовательные квалификаторы
Тип функции с cv или ref (с C++11) (включая тип, именованный именем typedef) может появляться только как:
- тип функции для нестатического метода-члена,
- тип функции, к которому относится указатель на член,
- тип функции верхнего уровня объявления функции typedef или объявления псевдонима типа(с C++11),
- идентификатор типа в аргументе по умолчанию шаблона параметра типа, или
- идентификатор типа аргумента шаблона для параметра типа шаблона.
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Определение функции
Определение нечленной функции может появляться только в области видимости пространства имен (вложенных функций нет). Определение метода-члена также может появляться в теле определения класса. У них следующий синтаксис:
| attr (необязательно) decl-specifier-seq (необязательно) declarator virt-specifier-seq (необязательно) function-body |
где function-body — одно из следующих
| ctor-initializer (необязательно) compound-statement | (1) | |
| function-try-block | (2) | |
= delete ; | (3) | (с C++11) |
= default ; | (4) | (с C++11) |
| attr | - | (с C++11) список атрибутов. Эти атрибуты комбинируются с атрибутами после идентификатора в declarator (см. верхнюю часть этой страницы), если таковые имеются. |
| decl-specifier-seq | - | тип возвращаемого значения со спецификаторами, как в грамматике объявления |
| declarator | - | декларатор функции, такой же, как в грамматике объявления функции выше (может быть заключён в скобки). как и в случае с объявлением функции, за ним может следовать requires-clause(с C++20) |
| virt-specifier-seq | - | (с C++11) override, final или их комбинация в любом порядке (разрешается только для нестатических методов-членов) |
| ctor-initializer | - | список инициализаторов членов, разрешается только в конструкторах |
| compound-statement | - | заключённая в фигурные скобки последовательность операторов, которая образует тело функции |
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 { ... }Тело функции — сложный оператор (последовательность нуля или более операторов, заключённая в пару фигурных скобок), который выполняется при вызове функции.
Типы параметров, а также тип возвращаемого значения определения функции не могут быть (возможно, квалифицированными 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
} Удаленные функцииЕсли вместо тела функции используется специальный синтаксис Если функция перегружена, сначала выполняется разрешение перегрузки, и программа будет ошибочной только в том случае, если была выбрана удаленная функция: struct sometype
{
void* operator new(std::size_t) = delete;
void* operator new[](std::size_t) = delete;
};
sometype* p = new sometype; // error: attempts to call deleted sometype::operator newУдаленное определение функции должно быть первым объявлением в единице трансляции: ранее объявленная функция не может быть повторно объявлена как удаленная: struct sometype { sometype(); };
sometype::sometype() = 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;__func__Внутри тела функции, локальная предопределенная переменная static const char __func__[] = "function-name"; Эта переменная имеет область действия блока и статический срок жизни: struct S
{
S(): s(__func__) {} // okay: 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) |
Примечания
В случае неоднозначности между объявлением переменной с использованием синтаксиса прямой инициализации и объявлением функции, компилятор всегда выбирает объявление функции; см. прямую инициализацию.
| Макрос проверки функций | Значение | Std | Функция |
|---|---|---|---|
__cpp_decltype_auto | 201304L | (C++14) |
decltype(auto) |
__cpp_return_type_deduction | 201304L | (C++14) | Вывод типа возвращаемого значения для обычных функций |
Пример
#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;
}Возможный вывод:
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 2145 | C++98 | декларатор в определении функции не мог быть заключен в скобки | разрешено |
| CWG 2259 | C++11 | правило разрешения неоднозначностей относительно имен типов в скобках не охватывало лямбда-выражения | охвачено |
| CWG 2430 | C++98 | в определении функции-члена в определении класса, тип этого класса не мог быть типом возврата или типом параметра из-за разрешения CWG issue 1824 | проверку полноты осуществлять только в теле функции |
См. также
| Документация C по Объявлению функций |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/function