8 Declarators [dcl.decl]

8.3 Meaning of declarators [dcl.meaning]

8.3.5 Functions [dcl.fct]

In a declaration T D where D has the form

D1 ( parameter-declaration-clause ) cv-qualifier-seqopt
ref-qualifieropt exception-specificationopt attribute-specifier-seqopt

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 function of ( parameter-declaration-clause ) cv-qualifier-seqopt ref-qualifieropt returning T”. 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-seqopt
ref-qualifieropt exception-specificationopt attribute-specifier-seqopt 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 function of (parameter-declaration-clause) cv-qualifier-seqopt ref-qualifieropt returning trailing-return-type”. The optional attribute-specifier-seq appertains to the function type.

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. 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> contains a mechanism for accessing arguments passed using the ellipsis (see [expr.call] and [support.runtime]).  — end note ]

A single name can be used for several different functions in a single scope; this is function overloading (Clause [over]). All declarations for a function shall agree exactly in both the return type and the parameter-type-list. 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 Tor “function returning T” is adjusted to be “pointer to T” or “pointer to function returning T,” respectively. 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:


typedef int FIC(int) const;
FIC f;              // ill-formed: 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, and the cv-qualifier-seq, but not the default arguments ([dcl.fct.default]) or the exception specification ([except.spec]), 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 ]

If the type of a parameter includes a type of the form “pointer to array of unknown bound of T” or “reference to array of unknown bound of T,” the program is ill-formed.101 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.

Types shall not be defined in return or parameter types. The type of a parameter or the return type for a function definition shall not be an incomplete class type (possibly cv-qualified) unless the function is deleted ([dcl.fct.def.delete]) or the definition is nested within the member-specification for that class (including definitions in nested classes defined within the class).

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 { }           // ill-formed
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.proto]).  — end note ]

Example: the declaration

int i,
    (*pif)(const char*, const char*),

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);


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 ]

A declarator-id or abstract-declarator containing an ellipsis shall only be used in a parameter-declaration. Such a parameter-declaration is a parameter pack ([temp.variadic]). When it is part of a parameter-declaration-clause, the parameter pack is a function parameter pack ([temp.variadic]). [ Note: Otherwise, the parameter-declaration is part of a template-parameter-list and the parameter pack is a template parameter pack; see [temp.param].  — end note ] 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.102

As indicated by syntax, cv-qualifiers are a significant component in function return types.

This excludes parameters of type “ ptr-arr-seq T2” where T2 is “pointer to array of unknown bound of T” and where ptr-arr-seq means any sequence of “pointer to” and “array of” derived declarator types. This exclusion applies to the parameters of the function, and if a parameter is a pointer to function or pointer to member function then to its parameters also, etc.

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).