9 Declarations [dcl.dcl]

9.3 Declarators [dcl.decl]

9.3.3 Meaning of declarators [dcl.meaning]

9.3.3.5 Functions [dcl.fct]

In a declaration T D where D has the form
D1 ( parameter-declaration-clause ) cv-qualifier-seq
	ref-qualifier noexcept-specifier attribute-specifier-seq
and the type of the contained declarator-id in the declaration T D1 is “derived-declarator-type-list T”, the type of the declarator-id in D is “derived-declarator-type-list noexcept function of parameter-type-list cv-qualifier-seq ref-qualifier returning T”, where
The optional attribute-specifier-seq appertains to the function type.
In a declaration T D where D has the form
D1 ( parameter-declaration-clause ) cv-qualifier-seq
	ref-qualifier noexcept-specifier attribute-specifier-seq trailing-return-type
and the type of the contained declarator-id in the declaration T D1 is “derived-declarator-type-list T”, T shall be the single type-specifier auto.
The type of the declarator-id in D is “derived-declarator-type-list noexcept function of parameter-type-list cv-qualifier-seq ref-qualifier returning U”, where
The optional attribute-specifier-seq appertains to the function type.
A type of either form is a function type.87
parameter-declaration-clause:
	parameter-declaration-list ...
	parameter-declaration-list , ...
parameter-declaration-list:
	parameter-declaration
	parameter-declaration-list , parameter-declaration
parameter-declaration:
	attribute-specifier-seq decl-specifier-seq declarator
	attribute-specifier-seq decl-specifier-seq declarator = initializer-clause
	attribute-specifier-seq decl-specifier-seq abstract-declarator
	attribute-specifier-seq decl-specifier-seq abstract-declarator = initializer-clause
The optional attribute-specifier-seq in a parameter-declaration appertains to the parameter.
The parameter-declaration-clause determines the arguments that can be specified, and their processing, when the function is called.
Note
:
The parameter-declaration-clause is used to convert the arguments specified on the function call; see [expr.call].
— end note
 ]
If the parameter-declaration-clause is empty, the function takes no arguments.
A parameter list consisting of a single unnamed parameter of non-dependent type void is equivalent to an empty parameter list.
Except for this special case, a parameter shall not have type cv void.
A parameter with volatile-qualified type is deprecated; see [depr.volatile.type].
If the parameter-declaration-clause terminates with an ellipsis or a function parameter pack ([temp.variadic]), the number of arguments shall be equal to or greater than the number of parameters that do not have a default argument and are not function parameter packs.
Where syntactically correct and where “...” is not part of an abstract-declarator, “, ...” is synonymous with “....
Example
:
The declaration
int printf(const char*, ...);
declares a function that can be called with varying numbers and types of arguments.
printf("hello world");
printf("a=%d b=%d", a, b);
However, the first argument must be of a type that can be converted to a const char*.
— end example
 ]
Note
:
The standard header <cstdarg> ([cstdarg.syn]) contains a mechanism for accessing arguments passed using the ellipsis (see [expr.call] and [support.runtime]).
— end note
 ]
The type of a function is determined using the following rules.
The type of each parameter (including function parameter packs) is determined from its own decl-specifier-seq and declarator.
After determining the type of each parameter, any parameter of type “array of T” or of function type T is adjusted to be “pointer to T.
After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type.
The resulting list of transformed parameter types and the presence or absence of the ellipsis or a function parameter pack is the function's parameter-type-list.
Note
:
This transformation does not affect the types of the parameters.
For example, int(*)(const int p, decltype(p)*) and int(*)(int, const int*) are identical types.
— end note
 ]
A function type with a cv-qualifier-seq or a ref-qualifier (including a type named by typedef-name ([dcl.typedef], [temp.param])) shall appear only as:
Example
:
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
— end example
 ]
The effect of a cv-qualifier-seq in a function declarator is not the same as adding cv-qualification on top of the function type.
In the latter case, the cv-qualifiers are ignored.
Note
:
A function type that has a cv-qualifier-seq is not a cv-qualified type; there are no cv-qualified function types.
— end note
 ]
Example
:
typedef void F();
struct S {
  const F f;        // OK: equivalent to: void f();
};
— end example
 ]
The return type, the parameter-type-list, the ref-qualifier, the cv-qualifier-seq, and the exception specification, but not the default arguments ([dcl.fct.default]) or the trailing requires-clause ([dcl.decl]), are part of the function type.
Note
:
Function types are checked during the assignments and initializations of pointers to functions, references to functions, and pointers to member functions.
— end note
 ]
Example
:
The declaration
int fseek(FILE*, long, int);
declares a function taking three arguments of the specified types, and returning int ([dcl.type]).
— end example
 ]
A single name can be used for several different functions in a single scope; this is function overloading ([over]).
All declarations for a function shall have equivalent return types, parameter-type-lists, and requires-clauses ([temp.over.link]).
Functions shall not have a return type of type array or function, although they may have a return type of type pointer or reference to such things.
There shall be no arrays of functions, although there can be arrays of pointers to functions.
A volatile-qualified return type is deprecated; see [depr.volatile.type].
Types shall not be defined in return or parameter types.
A typedef of function type may be used to declare a function but shall not be used to define a function ([dcl.fct.def]).
Example
:
typedef void F();
F  fv;              // OK: equivalent to void fv();
F  fv { }           // error
void fv() { }       // OK: definition of fv
— end example
 ]
An identifier can optionally be provided as a parameter name; if present in a function definition ([dcl.fct.def]), it names a parameter.
Note
:
In particular, parameter names are also optional in function definitions and names used for a parameter in different declarations and the definition of a function need not be the same.
If a parameter name is present in a function declaration that is not a definition, it cannot be used outside of its function declarator because that is the extent of its potential scope ([basic.scope.param]).
— end note
 ]
Example
:
The declaration
int i,
    *pi,
    f(),
    *fpi(int),
    (*pif)(const char*, const char*),
    (*fpif(int))(int);
declares an integer i, a pointer pi to an integer, a function f taking no arguments and returning an integer, a function fpi taking an integer argument and returning a pointer to an integer, a pointer pif to a function which takes two pointers to constant characters and returns an integer, a function fpif taking an integer argument and returning a pointer to a function that takes an integer argument and returns an integer.
It is especially useful to compare fpi and pif.
The binding of *fpi(int) is *(fpi(int)), so the declaration suggests, and the same construction in an expression requires, the calling of a function fpi, and then using indirection through the (pointer) result to yield an integer.
In the declarator (*pif)(const char*, const char*), the extra parentheses are necessary to indicate that indirection through a pointer to a function yields a function, which is then called.
— end example
 ]
Note
:
Typedefs and trailing-return-types are sometimes convenient when the return type of a function is complex.
For example, the function fpif above could have been declared
typedef int  IFUNC(int);
IFUNC*  fpif(int);
or
auto fpif(int)->int(*)(int);
A trailing-return-type is most useful for a type that would be more complicated to specify before the declarator-id:
template <class T, class U> auto add(T t, U u) -> decltype(t + u);
rather than
template <class T, class U> decltype((*(T*)0) + (*(U*)0)) add(T t, U u);
— end note
 ]
A non-template function is a function that is not a function template specialization.
Note
:
A function template is not a function.
— end note
 ]
An abbreviated function template is a function declaration that has one or more generic parameter type placeholders ([dcl.spec.auto]).
An abbreviated function template is equivalent to a function template ([temp.fct]) whose template-parameter-list includes one invented type template-parameter for each generic parameter type placeholder of the function declaration, in order of appearance.
For a placeholder-type-specifier of the form auto, the invented parameter is an unconstrained type-parameter.
For a placeholder-type-specifier of the form type-constraint auto, the invented parameter is a type-parameter with that type-constraint.
The invented type template-parameter is a template parameter pack if the corresponding parameter-declaration declares a function parameter pack ([dcl.fct]).
If the placeholder contains decltype(auto), the program is ill-formed.
The adjusted function parameters of an abbreviated function template are derived from the parameter-declaration-clause by replacing each occurrence of a placeholder with the name of the corresponding invented template-parameter.
Example
:
template<typename T>     concept C1 = /* ... */;
template<typename T>     concept C2 = /* ... */;
template<typename... Ts> concept C3 = /* ... */;

void g1(const C1 auto*, C2 auto&);
void g2(C1 auto&...);
void g3(C3 auto...);
void g4(C3 auto);
These declarations are functionally equivalent (but not equivalent) to the following declarations.
template<C1 T, C2 U> void g1(const T*, U&);
template<C1... Ts>   void g2(Ts&...);
template<C3... Ts>   void g3(Ts...);
template<C3 T>       void g4(T);
Abbreviated function templates can be specialized like all function templates.
template<> void g1<int>(const int*, const double&); // OK, specialization of g1<int, const double>
— end example
 ]
An abbreviated function template can have a template-head.
The invented template-parameters are appended to the template-parameter-list after the explicitly declared template-parameters.
Example
:
template<typename> concept C = /* ... */;

template <typename T, C U>
  void g(T x, U y, C auto z);
This is functionally equivalent to each of the following two declarations.
template<typename T, C U, C W>
  void g(T x, U y, W z);

template<typename T, typename U, typename W>
  requires C<U> && C<W>
  void g(T x, U y, W z);
— end example
 ]
A function declaration at block scope shall not declare an abbreviated function template.
A declarator-id or abstract-declarator containing an ellipsis shall only be used in a parameter-declaration.
When it is part of a parameter-declaration-clause, the parameter-declaration declares a function parameter pack ([temp.variadic]).
Otherwise, the parameter-declaration is part of a template-parameter-list and declares a template parameter pack; see [temp.param].
A function parameter pack is a pack expansion ([temp.variadic]).
Example
:
template<typename... T> void f(T (* ...t)(int, int));

int add(int, int);
float subtract(int, int);

void g() {
  f(add, subtract);
}
— end example
 ]
There is a syntactic ambiguity when an ellipsis occurs at the end of a parameter-declaration-clause without a preceding comma.
In this case, the ellipsis is parsed as part of the abstract-declarator if the type of the parameter either names a template parameter pack that has not been expanded or contains auto; otherwise, it is parsed as part of the parameter-declaration-clause.88
As indicated by syntax, cv-qualifiers are a significant component in function return types.
One can explicitly disambiguate the parse either by introducing a comma (so the ellipsis will be parsed as part of the parameter-declaration-clause) or by introducing a name for the parameter (so the ellipsis will be parsed as part of the declarator-id).