9 Declarations [dcl.dcl]

9.5 Function definitions [dcl.fct.def]

9.5.1 In general [dcl.fct.def.general]

Any informal reference to the body of a function should be interpreted as a reference to the non-terminal function-body.
The optional attribute-specifier-seq in a function-definition appertains to the function.
In a function-definition, either void declarator ; or declarator ; shall be a well-formed function declaration as described in [dcl.fct].
A function shall be defined only in namespace or class scope.
The type of a parameter or the return type for a function definition shall not be a (possibly cv-qualified) class type that is incomplete or abstract within the function body unless the function is deleted ([dcl.fct.def.delete]).
[Example 1:
A simple example of a complete function definition is int max(int a, int b, int c) { int m = (a > b) ? a : b; return (m > c) ? m : c; }
Here int is the decl-specifier-seq; max(int a, int b, int c) is the declarator; { /* ... */ } is the function-body.
— end example]
A ctor-initializer is used only in a constructor; see [class.ctor] and [class.init].
[Note 1:
A cv-qualifier-seq affects the type of this in the body of a member function; see [expr.prim.this].
— end note]
[Note 2:
Unused parameters need not be named.
For example,
void print(int a, int) { std::printf("a = %d\n",a); } — end note]
A function-local predefined variable is a variable with static storage duration that is implicitly defined in a function parameter scope.
The function-local predefined variable __func__ is defined as if a definition of the form static const char __func__[] = "function-name"; had been provided, where function-name is an implementation-defined string.
It is unspecified whether such a variable has an address distinct from that of any other object in the program.90
[Example 2: struct S { S() : s(__func__) { } // OK const char* s; }; void f(const char* s = __func__); // error: __func__ is undeclared — end example]
Implementations are permitted to provide additional predefined variables with names that are reserved to the implementation ([lex.name]).
If a predefined variable is not odr-used ([basic.def.odr]), its string value need not be present in the program image.

9.5.2 Explicitly-defaulted functions [dcl.fct.def.default]

A function definition whose function-body is of the form = default ; is called an explicitly-defaulted definition.
A function that is explicitly defaulted shall
  • be a special member function or a comparison operator function ([over.binary]), and
  • not have default arguments.
The type T of an explicitly defaulted special member function F is allowed to differ from the type T it would have had if it were implicitly declared, as follows:
  • T and T may have differing ref-qualifiers;
  • T and T may have differing exception specifications; and
  • if T has a parameter of type const C&, the corresponding parameter of T may be of type C&.
If T differs from T in any other way, then:
  • if F is an assignment operator, and the return type of T differs from the return type of T or T's parameter type is not a reference, the program is ill-formed;
  • otherwise, if F is explicitly defaulted on its first declaration, it is defined as deleted;
  • otherwise, the program is ill-formed.
An explicitly-defaulted function that is not defined as deleted may be declared constexpr or consteval only if it is constexpr-compatible ([special], [class.compare.default]).
A function explicitly defaulted on its first declaration is implicitly inline ([dcl.inline]), and is implicitly constexpr ([dcl.constexpr]) if it is constexpr-compatible.
[Example 1: struct S { constexpr S() = default; // error: implicit S() is not constexpr S(int a = 0) = default; // error: default argument void operator=(const S&) = default; // error: non-matching return type ~S() noexcept(false) = default; // OK, despite mismatched exception specification private: int i; S(S&); // OK: private copy constructor }; S::S(S&) = default; // OK: defines copy constructor struct T { T(); T(T &&) noexcept(false); }; struct U { T t; U(); U(U &&) noexcept = default; }; U u1; U u2 = static_cast<U&&>(u1); // OK, calls std​::​terminate if T​::​T(T&&) throws — end example]
Explicitly-defaulted functions and implicitly-declared functions are collectively called defaulted functions, and the implementation shall provide implicit definitions for them ([class.ctor], [class.dtor], [class.copy.ctor], [class.copy.assign]), including possibly defining them as deleted.
A defaulted prospective destructor ([class.dtor]) that is not a destructor is defined as deleted.
A defaulted special member function that is neither a prospective destructor nor an eligible special member function ([special]) is defined as deleted.
A 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.
[Note 1:
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.
— end note]
[Example 2: struct trivial { trivial() = default; trivial(const trivial&) = default; trivial(trivial&&) = default; trivial& operator=(const trivial&) = default; trivial& operator=(trivial&&) = default; ~trivial() = default; }; struct nontrivial1 { nontrivial1(); }; nontrivial1::nontrivial1() = default; // not first declaration — end example]

9.5.3 Deleted definitions [dcl.fct.def.delete]

A function definition whose function-body is of the form = delete ; is called a deleted definition.
A function with a deleted definition is also called a deleted function.
A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed.
[Note 1:
This includes calling the function implicitly or explicitly and forming a pointer or pointer-to-member to the function.
It applies even for references in expressions that are not potentially-evaluated.
For an overload set, only the function selected by overload resolution is referenced.
The implicit odr-use ([basic.def.odr]) of a virtual function does not, by itself, constitute a reference.
— end note]
[Example 1:
One can prevent default initialization and initialization by non-doubles with struct onlydouble { onlydouble() = delete; // OK, but redundant template<class T> onlydouble(T) = delete; onlydouble(double); };
— end example]
[Example 2:
One can prevent use of a class in certain new-expressions by using deleted definitions of a user-declared operator new for that class.
struct sometype { void* operator new(std::size_t) = delete; void* operator new[](std::size_t) = delete; }; sometype* p = new sometype; // error: deleted class operator new sometype* q = new sometype[3]; // error: deleted class operator new[] — end example]
[Example 3:
One can make a class uncopyable, i.e., move-only, by using deleted definitions of the copy constructor and copy assignment operator, and then providing defaulted definitions of the move constructor and move assignment operator.
struct moveonly { moveonly() = default; moveonly(const moveonly&) = delete; moveonly(moveonly&&) = default; moveonly& operator=(const moveonly&) = delete; moveonly& operator=(moveonly&&) = default; ~moveonly() = default; }; moveonly* p; moveonly q(*p); // error: deleted copy constructor — end example]
A deleted function is implicitly an inline function ([dcl.inline]).
[Note 2:
The one-definition rule ([basic.def.odr]) applies to deleted definitions.
— end note]
A deleted definition of a function shall be the first declaration of the function or, for an explicit specialization of a function template, the first declaration of that specialization.
An implicitly declared allocation or deallocation function ([basic.stc.dynamic]) shall not be defined as deleted.
[Example 4: struct sometype { sometype(); }; sometype::sometype() = delete; // error: not first declaration — end example]

9.5.4 Coroutine definitions [dcl.fct.def.coroutine]

The parameter-declaration-clause of the coroutine shall not terminate with an ellipsis that is not part of a parameter-declaration.
[Example 1: task<int> f(); task<void> g1() { int i = co_await f(); std::cout << "f() => " << i << std::endl; } template <typename... Args> task<void> g2(Args&&...) { // OK, ellipsis is a pack expansion int i = co_await f(); std::cout << "f() => " << i << std::endl; } task<void> g3(int a, ...) { // error: variable parameter list not allowed int i = co_await f(); std::cout << "f() => " << i << std::endl; } — end example]
The promise type of a coroutine is std​::​coroutine_­traits<R, P, , P>​::​promise_­type, where R is the return type of the function, and are the sequence of types of the function parameters, preceded by the type of the implicit object parameter ([over.match.funcs]) if the coroutine is a non-static member function.
The promise type shall be a class type.
In the following, is an lvalue of type , where denotes *this and denotes the function parameter for a non-static member function, and denotes the function parameter otherwise.
A coroutine behaves as if its function-body were replaced by:
   promise-type promise promise-constructor-arguments ;
   try {
      co_await promise.initial_suspend() ;
   } catch ( ... ) {
      if (!initial-await-resume-called)
         throw ;
      promise.unhandled_exception() ;
final-suspend :
   co_await promise.final_suspend() ;
  • the await-expression containing the call to initial_­suspend is the initial suspend point, and
  • the await-expression containing the call to final_­suspend is the final suspend point, and
  • initial-await-resume-called is initially false and is set to true immediately before the evaluation of the await-resume expression ([expr.await]) of the initial suspend point, and
  • promise-type denotes the promise type, and
  • the object denoted by the exposition-only name promise is the promise object of the coroutine, and
  • the label denoted by the name final-suspend is defined for exposition only ([stmt.return.coroutine]), and
  • promise-constructor-arguments is determined as follows: overload resolution is performed on a promise constructor call created by assembling an argument list with lvalues .
    If a viable constructor is found ([over.match.viable]), then promise-constructor-arguments is (p, , p), otherwise promise-constructor-arguments is empty.
If searches for the names return_­void and return_­value in the scope of the promise type each find any declarations, the program is ill-formed.
[Note 1:
If return_­void is found, flowing off the end of a coroutine is equivalent to a co_­return with no operand.
Otherwise, flowing off the end of a coroutine results in undefined behavior ([stmt.return.coroutine]).
— end note]
The expression promise.get_­return_­object() is used to initialize the glvalue result or prvalue result object of a call to a coroutine.
The call to get_­return_­object is sequenced before the call to initial_­suspend and is invoked at most once.
A suspended coroutine can be resumed to continue execution by invoking a resumption member function ([coroutine.handle.resumption]) of a coroutine handle ([coroutine.handle]) that refers to the coroutine.
The function that invoked a resumption member function is called the resumer.
Invoking a resumption member function for a coroutine that is not suspended results in undefined behavior.
An implementation may need to allocate additional storage for a coroutine.
This storage is known as the coroutine state and is obtained by calling a non-array allocation function ([basic.stc.dynamic.allocation]).
The allocation function's name is looked up by searching for it in the scope of the promise type.
  • If any declarations are found, overload resolution is performed on a function call created by assembling an argument list.
    The first argument is the amount of space requested, and has type std​::​size_­t.
    The lvalues are the succeeding arguments.
  • Otherwise, a search is performed in the global scope.
If no viable function is found ([over.match.viable]), overload resolution is performed again on a function call created by passing just the amount of space required as an argument of type std​::​size_­t.
If a search for the name get_­return_­object_­on_­allocation_­failure in the scope of the promise type ([class.member.lookup]) finds any declarations, then the result of a call to an allocation function used to obtain storage for the coroutine state is assumed to return nullptr if it fails to obtain storage, and if a global allocation function is selected, the ​::​operator new(size_­t, nothrow_­t) form is used.
The allocation function used in this case shall have a non-throwing noexcept-specifier.
If the allocation function returns nullptr, the coroutine returns control to the caller of the coroutine and the return value is obtained by a call to T​::​get_­return_­object_­on_­allocation_­failure(), where T is the promise type.
[Example 2: #include <iostream> #include <coroutine> // ​::​operator new(size_­t, nothrow_­t) will be used if allocation is needed struct generator { struct promise_type; using handle = std::coroutine_handle<promise_type>; struct promise_type { int current_value; static auto get_return_object_on_allocation_failure() { return generator{nullptr}; } auto get_return_object() { return generator{handle::from_promise(*this)}; } auto initial_suspend() { return std::suspend_always{}; } auto final_suspend() noexcept { return std::suspend_always{}; } void unhandled_exception() { std::terminate(); } void return_void() {} auto yield_value(int value) { current_value = value; return std::suspend_always{}; } }; bool move_next() { return coro ? (coro.resume(), !coro.done()) : false; } int current_value() { return coro.promise().current_value; } generator(generator const&) = delete; generator(generator && rhs) : coro(rhs.coro) { rhs.coro = nullptr; } ~generator() { if (coro) coro.destroy(); } private: generator(handle h) : coro(h) {} handle coro; }; generator f() { co_yield 1; co_yield 2; } int main() { auto g = f(); while (g.move_next()) std::cout << g.current_value() << std::endl; } — end example]
The coroutine state is destroyed when control flows off the end of the coroutine or the destroy member function ([coroutine.handle.resumption]) of a coroutine handle ([coroutine.handle]) that refers to the coroutine is invoked.
In the latter case, control in the coroutine is considered to be transferred out of the function ([stmt.dcl]).
The storage for the coroutine state is released by calling a non-array deallocation function ([basic.stc.dynamic.deallocation]).
If destroy is called for a coroutine that is not suspended, the program has undefined behavior.
The deallocation function's name is looked up by searching for it in the scope of the promise type.
If nothing is found, a search is performed in the global scope.
If both a usual deallocation function with only a pointer parameter and a usual deallocation function with both a pointer parameter and a size parameter are found, then the selected deallocation function shall be the one with two parameters.
Otherwise, the selected deallocation function shall be the function with one parameter.
If no usual deallocation function is found, the program is ill-formed.
The selected deallocation function shall be called with the address of the block of storage to be reclaimed as its first argument.
If a deallocation function with a parameter of type std​::​size_­t is used, the size of the block is passed as the corresponding argument.
When a coroutine is invoked, after initializing its parameters ([expr.call]), a copy is created for each coroutine parameter.
For a parameter of type cv T, the copy is a variable of type cv T with automatic storage duration that is direct-initialized from an xvalue of type T referring to the parameter.
[Note 2:
An original parameter object is never a const or volatile object ([basic.type.qualifier]).
— end note]
The initialization and destruction of each parameter copy occurs in the context of the called coroutine.
Initializations of parameter copies are sequenced before the call to the coroutine promise constructor and indeterminately sequenced with respect to each other.
The lifetime of parameter copies ends immediately after the lifetime of the coroutine promise object ends.
[Note 3:
If a coroutine has a parameter passed by reference, resuming the coroutine after the lifetime of the entity referred to by that parameter has ended is likely to result in undefined behavior.
— end note]
If the evaluation of the expression promise.unhandled_­exception() exits via an exception, the coroutine is considered suspended at the final suspend point.
The expression co_­await promise.final_­suspend() shall not be potentially-throwing ([except.spec]).