A function declaration introduces the function name and its type. A function definition associates the function name/type with the function body.
Function declarations may appear in any scope. A function declaration at class scope introduces a class member function (unless the friend specifier is used), see member functions and friend functions for details.
The type of the function being declared is composed from the return type (provided by the decl-specifier-seq of the declaration syntax) and the function declarator.
noptr-declarator ( parameter-list ) cv(optional) ref(optional) except(optional) attr(optional) | (1) | |
noptr-declarator ( parameter-list ) cv(optional) ref(optional) except(optional) attr(optional) -> trailing | (2) | (since C++11) |
(see Declarations for the other forms of the declarator syntax).
auto
noptr-declarator | - | any valid declarator, but if it begins with *, &, or &&, it has to be surrounded by parentheses. | ||||||||
parameter-list | - | possibly empty, comma-separated list of the function parameters (see below for details) | ||||||||
attr | - | (since C++11) optional list of attributes. These attributes are applied to the type of the function, not the function itself. The attributes for the function appear after the identifier within the declarator and are combined with the attributes that appear in the beginning of the declaration, if any. | ||||||||
cv | - | const/volatile qualification, only allowed in non-static member function declarations | ||||||||
ref | - | (since C++11) ref-qualification, only allowed in non-static member function declarations | ||||||||
except | - |
|
||||||||
trailing | - | Trailing return type, useful if the return type depends on argument names, such as template<class T, class U> auto add(T t, U u) -> decltype(t + u); or is complicated, such as in auto fpif(int)->int(*)(int) |
As mentioned in Declarations, the declarator can be followed by a requires clause, which declares the associated constraints for the function, which must be satisfied in order for the function to be selected by overload resolution. (example: | (since C++20) |
Function declarators can be mixed with other declarators, where decl-specifier-seq allows:
// 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 };
Using a volatile-qualified object type as parameter type or return type is deprecated. | (since C++20) |
The return type of a function cannot be a function type or an array type (but can be a pointer or reference to those).
As with any declaration, attributes that appear before the declaration and the attributes that appear immediately after the identifier within the declarator both apply to the entity being declared or defined (in this case, to the function): [[noreturn]] void f [[noreturn]] (); // okay: both attributes apply to the function f However, the attributes that appear after the declarator (in the syntax above), apply to the type of the function, not to the function itself: void f() [[noreturn]]; // error: this attribute has no effect on the function itself | (since C++11) |
As with any declaration, the type of the function func
declared as ret func(params)
is ret(params)
(except for parameter type rewriting described below): see type naming.
Return type deductionIf the decl-specifier-seq of the function declaration contains the keyword int x = 1; auto f() { return x; } // return type is int const auto& f() { return x; } // return type is const int& If the return type is 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)) (note: " If there are multiple return statements, they must all deduce to the same type: auto f(bool val) { if (val) return 123; // deduces return type int else return 3.14f; // error: deduces return type float } If there is no return statement or if the argument of the return statement is a void expression, the declared return type must be either auto f() {} // returns void auto g() { return f(); } // returns void auto* x() {} // error: cannot deduce auto* from void Once a return statement has been seen in a function, the return type deduced from that statement can be used in the rest of the function, including in other return statements: 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 } If the return statement uses a brace-init-list, deduction is not allowed: auto func () { return {1, 2, 3}; } // error Virtual functions and coroutines (since C++20)cannot use return type deduction: struct F { virtual auto f() { return 2; } // error }; Function templates other than user-defined conversion functions can use return type deduction. The deduction takes place at instantiation even if the expression in the return statement is not dependent. This instantiation is not in an immediate context for the purposes of 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 Redeclarations or specializations of functions or function templates that use return type deduction must use the same return type placeholders: 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 Similarly, redeclarations or specializations of functions or function templates that do not use return type deduction must not use a placeholder: 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 Explicit instantiation declarations do not themselves instantiate function templates that use return type deduction: 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 | (since C++14) |
Parameter list determines the arguments that can be specified when the function is called. It is a comma-separated list of parameter declarations, each of which has the following syntax:
attr(optional) decl-specifier-seq declarator | (1) | |
attr(optional) decl-specifier-seq declarator = initializer | (2) | |
attr(optional) decl-specifier-seq abstract-declarator(optional) | (3) | |
attr(optional) decl-specifier-seq abstract-declarator(optional) = 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);
and int f();
declare the same function. Note that the type void
(possibly cv-qualified) cannot be used in a parameter list otherwise: int f(void, int);
and int f(const void);
are errors (although derived types, such as void*
can be used). In a template, only non-dependent void type can be used (a function taking a single parameter of type T
does not become a no-parameter function if instantiated with T = void
).An ellipsis ...
may appear at the end of the parameter list; this declares a variadic function:
int printf(const char* fmt ...);
For compatibility with C89, an optional comma may appear before the ellipsis if the parameter list contains at least one parameter:
int printf(const char* fmt, ...); // OK, same as above
Although decl-specifier-seq implies there can exist specifiers other than type specifiers, the only other specifier allowed is | (until C++17) |
If any of the function parameters uses a placeholder (either void f1(auto); // same as template<class T> void f(T) void f2(C1 auto); // same as template<C1 T> void f7(T), if C1 is a concept | (since C++20) |
Parameter names declared in function declarations are usually for only self-documenting purposes. They are used (but remain optional) in function definitions.
The type of each function parameter in the parameter list is determined according to the following rules:
int f(const int p, decltype(p)*);
and int f(int, const int*);
declare the same function)Because of these rules, the following function declarations declare exactly the same function:
int f(char s[3]); int f(char[]); int f(char* s); int f(char* const); int f(char* volatile s);
The following declarations also declare exactly the same function:
int f(int()); int f(int (*g)());
An ambiguity arises in a parameter list when a type name is nested in parentheses (including lambda expressions) (since C++11). In this case, the choice is between the declaration of a parameter of type pointer to function and the declaration of a parameter with redundant parentheses around the identifier of the declarator. The resolution is to consider the type name as a simple type specifier (which is the pointer to function type):
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]);
Parameter type cannot be a type that includes a reference or a pointer to array of unknown bound, including a multi-level pointers/arrays of such types, or a pointer to functions whose parameters are such types.
The ellipsis that indicates variadic arguments need not be preceded by a comma, even if it follows the ellipsis that indicates a parameter pack expansion, so the following function templates are exactly the same: template<typename... Args> void f(Args..., ...); template<typename... Args> void f(Args... ...); template<typename... Args> void f(Args......); An example of when such declaration might be used is the possible implementation of #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'); } Output: 2m•3m•7m = 42m³ | (since C++11) |
A non-member function definition may appear at namespace scope only (there are no nested functions). A member function definition may also appear in the body of a class definition. They have the following syntax:
attr(optional) decl-specifier-seq(optional) declarator virt-specifier-seq(optional) function-body |
where function-body is one of the following.
ctor-initializer(optional) compound-statement | (1) | |
function-try-block | (2) | |
= delete ; | (3) | (since C++11) |
= default ; | (4) | (since C++11) |
attr | - | (since C++11) optional list of attributes. These attributes are combined with the attributes after the identifier in the declarator (see top of this page), if any. |
decl-specifier-seq | - | the return type with specifiers, as in the declaration grammar |
declarator | - | function declarator, same as in the function declaration grammar above (can be parenthesized). as with function declaration, it may be followed by a requires-clause (since C++20) |
virt-specifier-seq | - | (since C++11) override , final , or their combination in any order (only allowed for non-static member functions) |
ctor-initializer | - | member initializer list, only allowed in constructors |
compound-statement | - | the brace-enclosed sequence of statements that constitutes the body of a function |
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 { ... }
The function body is a compound statement (sequence of zero or more statements surrounded by a pair of curly braces), which is executed when the function call is made.
The parameter types, as well as the return type of a function definition cannot be (possibly cv-qualified) incomplete class types unless the function is defined as deleted (since C++11). The completeness check is only made in the function body, which allows member functions to return the class in which they are defined (or its enclosing class), even if it is incomplete at the point of definition (it is complete in the function body).
The parameters declared in the declarator of a function definition are in scope within the body. If a parameter is not used in the function body, it does not need to be named (it's sufficient to use an abstract declarator):
void print(int a, int) // second parameter is not used { std::printf("a = %d\n", a); }
Even though top-level cv-qualifiers on the parameters are discarded in function declarations, they modify the type of the parameter as visible in the body of a function:
void f(const int n) // declares function of type void(int) { // but in the body, the type of n is const int }
Deleted functionsIf, instead of a function body, the special syntax If the function is overloaded, overload resolution takes place first, and the program is only ill-formed if the deleted function was selected: 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 The deleted definition of a function must be the first declaration in a translation unit: a previously-declared function cannot be redeclared as deleted: struct sometype { sometype(); }; sometype::sometype() = delete; // error: must be deleted on the first declaration User-provided functionsA function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration. A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted; if such a function is implicitly defined as deleted, the program is ill-formed. Declaring a function as defaulted after its first declaration can provide efficient execution and concise definition while enabling a stable binary interface to an evolving code base. // 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__Within the function body, the function-local predefined variable static const char __func__[] = "function-name"; This variable has block scope and static storage duration: 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; } Possible output: Foo Bar Pub ~Bar | (since C++11) |
In case of ambiguity between a variable declaration using the direct-initialization syntax and a function declaration, the compiler always chooses function declaration; see direct-initialization.
#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; }
Possible output:
stoi() failed! Hello, world! Hello, test! Hello, again! f2("bad"): 0 f2("42"): 42
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 135 | C++98 | member functions defined in class could not have a parameter of or return its own class because it is incomplete | allowed |
CWG 332 | C++98 | a parameter could have cv-qualified void type | prohibited |
CWG 393 | C++98 | types that include pointers/references to array of unknown bound could not be parameters | such types are allowed |
CWG 452 | C++98 | member initializer list was not a part of function body | made it a part of function body by modifying the syntax of function definition |
CWG 577 | C++98 | dependent type void could be used todeclare a function taking no parameters | only non-dependentvoid is allowed |
CWG 1327 | C++11 | defaulted or deleted functions could not be specified with override or final | allowed |
CWG 1355 | C++11 | only special member functions could be user-provided | extended to all functions |
CWG 1394 | C++11 | deleted functions could not have any parameter of an incomplete type or return an incomplete type | incomplete type allowed |
CWG 1824 | C++98 | the completeness check on parameter type and return type of a function definition could be made outside the context of the function definition | only make the completeness check in the context of the function definition |
CWG 1877 | C++14 | return type deduction treated return; as return void(); | simply deduce the return type as void in this case |
CWG 2015 | C++11 | the implicit odr-use of a deleted virtual function was ill-formed | such odr-uses are exempt from the use prohibition |
CWG 2044 | C++14 | return type deduction on functions returning void would fail if the declared return type is decltype(auto) | updated the deduction rule to handle this case |
CWG 2081 | C++14 | function redeclarations could use return type deduction even if the initial declaration does not | not allowed |
CWG 2145 | C++98 | the declarator in function definition could not be parenthesized | allowed |
CWG 2259 | C++11 | the ambiguity resolution rule regarding parenthesized type names did not cover lambda expressions | covered |
CWG 2430 | C++98 | in the definition of a member function in a class definition, the type of that class could not be the return type or parameter type due to the resolution of CWG issue 1824 | only make the completeness check in the function body |
C documentation for Declaring functions |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/function