A function declaration introduces the function name and its type. A function definition associates the function name/type with the function body.
Function declaration
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
and
for details.
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
for the other forms of the declarator syntax)
1) Regular function declarator syntax.
2) Trailing return type declaration. The decl-specifier-seq in this case must contain the keyword 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) a list of
. 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<classT,classU>autoadd(Tt,Uu)->decltype(t+u); or is complicated, such as in autofpif(int)->int(*)(int)As mentioned in
, the declarator can be followed by a
, which declares the associated
for the function, which must be satisfied in order for the function to be selected by
. (example: voidf1(inta)requirestrue;) Note that the associated constraint is part of function signature, but not part of function type.
(since C++20)Function declarators can be mixed with other declarators, where the
declaration specifier sequence
allows:
// declares an int, an int*, a function, and a pointer to a functioninta=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 intstructS{virtualintf(char)const,g(int)&&;// declares two non-static member functionsvirtualintf(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]]voidf[[noreturn]]();// OK: both attributes apply to the function fHowever, the attributes that appear after the declarator (in the syntax above), apply to the type of the function, not to the function itself:
voidf()[[noreturn]];// Error: this attribute has no effect on the function itself(since C++11)Return type deduction
If the decl-specifier-seq of the function declaration contains the keyword auto, trailing return type may be omitted, and will be deduced by the compiler from the type of the operand used in the
statement. If the return type does not use decltype(auto), the deduction follows the rules of
:
intx=1;autof(){returnx;}// return type is intconstauto&f(){returnx;}// return type is const int&If the return type is decltype(auto), the return type is as what would be obtained if the operand used in the return statement were wrapped in
:
intx=1;decltype(auto)f(){returnx;}// return type is int, same as decltype(x)decltype(auto)f(){return(x);}// return type is int&, same as decltype((x))(note: “constdecltype(auto)&” is an error, decltype(auto) must be used on its own)
If there are multiple return statements, they must all deduce to the same type:
autof(boolval){if(val)return123;// deduces return type intelsereturn3.14f;// Error: deduces return type float}If there is no return statement or if the operand of the return statement is a void expression, the declared return type must be either decltype(auto), in which case the deduced return type is void, or (possibly cv-qualified) auto, in which case the deduced return type is then (identically cv-qualified) void:
autof(){}// returns voidautog(){returnf();}// returns voidauto*x(){}// Error: cannot deduce auto* from voidOnce 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:
autosum(inti){if(i==1)returni;// sum’s return type is intelsereturnsum(i-1)+i;// OK: sum’s return type is already known}If the return statement uses a
brace-enclosed initializer list
, deduction is not allowed:
autofunc(){return{1,2,3};}// Error
and
(since C++20) cannot use return type deduction:
structF{virtualautof(){return2;}// Error};
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
. This instantiation is not in an immediate context for the purposes of
.
template<classT>autof(Tt){returnt;}typedefdecltype(f(1))fint_t;// instantiates f<int> to deduce return typetemplate<classT>autof(T*t){return*t;}voidg(){int(*p)(int*)=&f;}// instantiates both fs to determine return types,// chooses second template overloadRedeclarations or specializations of functions or function templates that use return type deduction must use the same return type placeholders:
autof(intnum){returnnum;}// int f(int num); // Error: no placeholder return type// decltype(auto) f(int num); // Error: different placeholdertemplate<typenameT>autog(Tt){returnt;}templateautog(int);// OK: return type is int// template char g(char); // Error: not a specialization of the primary template gSimilarly, redeclarations or specializations of functions or function templates that do not use return type deduction must not use a placeholder:
intf(intnum);// auto f(int num) { return num; } // Error: not a redeclaration of ftemplate<typenameT>Tg(Tt){returnt;}templateintg(int);// OK: 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<typenameT>autof(Tt){returnt;}externtemplateautof(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
The 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-seqdeclarator(1) attr(optional)thisdecl-specifier-seqdeclarator
(2) (since C++23)attr(optional)decl-specifier-seqdeclarator=initializer(3) attr(optional)decl-specifier-seqabstract-declarator(optional)(4) attr(optional)thisdecl-specifier-seqabstract-declarator(optional)
(5) (since C++23)attr(optional)decl-specifier-seqabstract-declarator(optional)=initializer(6) void(7) 1) Declares a named (formal) parameter. For the meanings of decl-specifier-seq and declarator, see
.
intf(inta,int*p,int(*(*x)(double))[3]);
2) Declares a named
.
3) Declares a named (formal) parameter with a
.
intf(inta=7,int*p=nullptr,int(*(*x)(double))[3]=nullptr);
4) Declares an unnamed parameter.
intf(int,int*,int(*(*)(double))[3]);
5) Declares a unnamed
.
6) Declares an unnamed parameter with a
.
intf(int=7,int*=nullptr,int(*(*)(double))[3]=nullptr);
7) Indicates that the function takes no parameters, it is the exact synonym for an empty parameter list: intf(void); and intf(); declare the same function.
void is the only syntax equivalent to an empty parameter list, other usages of void parameters are ill-formed:
Incorrect usage Example multiple parameters are present intf1(void,int);the void parameter is named inff2(voidparam);void is cv-qualified intf3(constvoid);void is
intf4(T); (where T is void) the void parameter is an
(since C++23)intf5(thisvoid);Although decl-specifier-seq implies there can exist
other than type specifiers, the only other specifier allowed is register as well as auto(until C++11), and it has no effect.
(until C++17)If any of the function parameters uses a placeholder (either auto or a
), the function declaration is instead an
declaration:
voidf1(auto);// same as template<class T> void f1(T)voidf2(C1auto);// same as template<C1 T> void f2(T), if C1 is a concept(since C++20) A parameter declaration with the specifier this (syntax (
)/(
)) declares an explicit object parameter.
An explicit object parameter cannot be a
, and it can only appear as the first parameter of the parameter list in the following declarations:
a declaration of a
or member function template
an
or
of a templated member function
a
declaration
A member function with an explicit object parameter has the following restrictions:
The function is not
.
The function is not
.
The declarator of the function does not contain cv and ref.
structC{voidf(thisC&self);// OKtemplate<typenameSelf>voidg(thisSelf&&self);// also OK for templatesvoidp(thisC)const;// Error: “const” not allowed herestaticvoidq(thisC);// Error: “static” not allowed herevoidr(int,thisC);// Error: an explicit object parameter// can only be the first parameter};// void func(this C& self); // Error: non-member functions cannot have// an explicit object parameter(since C++23)Parameter names declared in function declarations are usually for only self-documenting purposes. They are used (but remain optional) in function definitions.
An ambiguity arises in a parameter list when a type name is nested in parentheses (including
)(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
(which is the pointer to function type):
classC{};voidf(int(C)){}// void f(int(*fp)(C param)) {}// NOT void f(int C) {}voidg(int*(C[10]));// void g(int *(*fp)(C param[10]));// NOT void g(int *C[10]);A type cannot be defined in the declaration of a function's parameter or in return type:
voidf(structS1{}s);// ErrorstructS2{}g();// ErrorParameter 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.
Using an ellipsis
The last parameter in the parameter list can be an ellipsis (...); this declares a
. The comma preceding the ellipsis can be omitted(deprecated in C++26):
intprintf(constchar*fmt,...);// a variadic functionintprintf(constchar*fmt...);// same as above, but deprecated since C++26template<typename...Args>voidf(Args...,...);// a variadic function template with a parameter packtemplate<typename...Args>voidf(Args......);// same as above, but deprecated since C++26template<typename...Args>voidf(Args......);// same as above, but deprecated since C++26Function type
A function's type is formed from the presence or absence of noexcept (present iff except is non-throwing), (since C++17)ref, (since C++11)cv, its parameter-type-list (see below), and its return type.
Parameter-type-list
A function’s parameter-type-list is determined as follows:
The type of each parameter (including function
)(since C++11) is determined from its own
.
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
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
or a function
(since C++11) is the function’s parameter-type-list.
voidf(char*);// #1voidf(char[]){}// defines #1voidf(constchar*){}// OK, another overloadvoidf(char*const){}// Error: redefines #1voidg(char(*)[2]);// #2voidg(char[3][2]){}// defines #2voidg(char[3][3]){}// OK, another overloadvoidh(intx(constint));// #3voidh(int(*)(int)){}// defines #3Trailing qualifiers
A function type with cv or ref(since C++11) (including a type named by
name) can appear only as:
the function type for a
,
the function type to which a pointer to member refers,
the top-level function type of a function typedef declaration or
(since C++11),
the
in the default argument of a
,
the type-id of a template argument for a template type parameter,
typedefintFIC(int)const;FICf;// Error: does not declare a member functionstructS{FICf;// OK};FICS::*pm=&S::f;// OKconstexprautoyeti=^^void(int)const&;// OK (since C++26)Function signature
Every function has a signature.
The signature of a function consists of its name and
. Its signature also contains the enclosing
, with the following exceptions:
If the function is a
, its signature contains the class of which the function is a member instead of the enclosing namespace. Its signature also contains the following components, if exists:
cv
ref
(since C++11)trailing requires clause
If the function is a non-template
function with a trailing requires clause, its signature contains the enclosing class instead of the enclosing namespace. The signature also contains the trailing requires clause.
(since C++20)except and attr(since C++11) doesn't involve function signature, although
affects the function type(since C++17).
Function definition
A non-member function definition may appear at namespace scope only (there are no nested functions). A
definition may also appear in the body of a
. They have the following syntax:
attr(optional)decl-specifier-seq(optional)declarator
virt-specs(optional)contract-specs(optional)function-body(1) attr(optional)decl-specifier-seq(optional)declarator
requires-clausecontract-specs(optional)function-body(2) (since C++20)1) A function definition without constraints.
2) A function definition with constraints.
attr- (since C++11) a list of
. 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
declarator- function declarator, same as in the function declaration grammar above (can be parenthesized) virt-specs- (since C++11)
,
, or their combination in any order requires-clause- a
contract-specs- (since C++26) a list of
function-body- the function body (see below)
function-body is one of the following:
ctor-initializer(optional)compound-statement(1) function-try-block(2) =default;(3) (since C++11)=delete;(4) (since C++11)=delete(string-literal);(5) (since C++26)1) Regular function body.
3) Explicitly defaulted function definition.
4) Explicitly deleted function definition.
5) Explicitly deleted function definition with error message.
ctor-initializer-
, only allowed in constructors compound-statement- the brace-enclosed
that constitutes the body of a function function-try-block- a
string-literal- an
that could be used to explain the rationale for why the function is deleted intmax(inta,intb,intc){intm=(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
(sequence of zero or more statements surrounded by a pair of curly braces), which is executed when the function call is made. Moreover, the function body of a
also includes the following:
For all non-static data members whose identifiers are absent in the constructor's
, the
or(since C++11)
used to initialize the corresponding member
.
For all base classes whose type names are absent in the constructor's member initializer list, the default-initializations used to initialize the corresponding base class subobjects.
If a function definition contains a virt-specs, it must define a
.
(since C++11)If a function definition contains a requires-clause, it must define a
.
(since C++20)voidf()override{}// Error: not a member functionvoidg()requires(sizeof(int)==4){}// Error: not a templated functionThe parameter types, as well as the return type of a function definition cannot be (possibly cv-qualified)
unless the function is defined as deleted(since C++11). The completeness check is only made in the function body, which allows
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
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):
voidprint(inta,int)// second parameter is not used{std::printf("a = %d\n",a);}Even though top-level
on the parameters are discarded in function declarations, they modify the type of the parameter as visible in the body of a function:
voidf(constintn)// declares function of type void(int){// but in the body, the type of “n” is const int}Defaulted functions
If the function definition is of syntax (
), the function is defined as explicitly defaulted.
A function that is explicitly defaulted must be a
or
(since C++20), and it must have no
.
An explicitly defaulted special member function F1 is allowed to differ from the corresponding special member function F2 that would have been implicitly declared, as follows:
F1 and F2 may have different ref and/or except.
If F2 has a non-object parameter of type constC&, the corresponding non-object parameter of F1 maybe of type C&.
If F2 has an implicit object parameter of type “reference to C”, F1 may be an explicit object member function whose
is of (possibly different) type “reference to C”, in which case the type of F1 would differ from the type of F2 in that the type of F1 has an additional parameter.
(since C++23)If the type of F1 differs from the type of F2 in a way other than as allowed by the preceding rules, then:
If F1 is an assignment operator, and the return type of F1 differs from the return type of F2 or F1’s non-object parameter type is not a reference, the program is ill-formed.
Otherwise, if F1 is explicitly defaulted on its first declaration, it is defined as deleted.
Otherwise, the program is ill-formed.
A function explicitly defaulted on its first declaration is implicitly
, and is implicitly constexpr if it can be a
.
structS{S(inta=0)=default;// error: default argumentvoidoperator=(constS&)=default;// error: non-matching return type~S()noexcept(false)=default;// OK, different exception specificationprivate:inti;S(S&);// OK, private copy constructor};S::S(S&)=default;// OK, defines copy constructorExplicitly-defaulted functions and implicitly-declared functions are collectively called defaulted functions. Their actual definitions will be implicitly provided, see their corresponding pages for details.
Deleted functions
If the function definition is of syntax (
) or (
)(since C++26), the function is defined as explicitly deleted.
Any use of a deleted function other than as the operand of
(since C++26) is ill-formed (the program will not compile). This includes calls, both explicit (with a function call operator) and implicit (a call to deleted overloaded operator, special member function, allocation function, etc), constructing a pointer or pointer-to-member to a deleted function, and even the use of a deleted function in an expression that is not
.
A non-pure virtual member function can be defined as deleted, even though it is implicitly
. A deleted function can only be overridden by deleted functions, and a non-deleted function can only be overridden by non-deleted functions.
If string-literal is present, the implementation is encouraged to include the text of it as part of the resulting diagnostic message which shows the rationale for deletion or to suggest an alternative.
(since C++26)If the function is overloaded,
takes place first, and the program is only ill-formed if the deleted function was selected:
structT{void*operatornew(std::size_t)=delete;void*operatornew[](std::size_t)=delete("new[] is deleted");// since C++26};T*p=newT;// Error: attempts to call deleted T::operator newT*p=newT[5];// Error: attempts to call deleted T::operator new[],// emits a diagnostic message “new[] is deleted”The deleted definition of a function must be the first declaration in a translation unit: a previously-declared function cannot be redeclared as deleted:
structT{T();};T::T()=delete;// Error: must be deleted on the first declarationUser-provided functions
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. 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-providedstructtrivial{trivial()=default;trivial(consttrivial&)=default;trivial(trivial&&)=default;trivial&operator=(consttrivial&)=default;trivial&operator=(trivial&&)=default;~trivial()=default;};structnontrivial{nontrivial();// first declaration};// not defaulted on the first declaration,// it is user-provided and is defined herenontrivial::nontrivial()=default;Ambiguity Resolution
In the case of an ambiguity between a function body and an
beginning with { or =(since C++26), the ambiguity is resolved by checking the type of the
of noptr-declarator :
If the type is a function type, the ambiguous token sequence is treated as a function body.
Otherwise, the ambiguous token sequence is treated as an initializer.
usingT=void();// function typeusingU=int;// non-function typeTa{};// defines a function doing nothingUb{};// value-initializes an int objectTc=delete("hello");// defines a function as deletedUd=delete("hello");// copy-initializes an int object with// the result of a delete expression (ill-formed)__func__
Within the function body, the function-local predefined variable __func__ is defined as if by
staticconstchar__func__[]="function-name";This variable has block scope and static storage duration:
structS{S():s(__func__){}// OK: initializer-list is part of function bodyconstchar*s;};voidf(constchar*s=__func__);// Error: parameter-list is part of declaratorRun this code
#include<iostream>voidFoo(){std::cout<<__func__<<' ';}structBar{Bar(){std::cout<<__func__<<' ';}~Bar(){std::cout<<__func__<<' ';}structPub{Pub(){std::cout<<__func__<<' ';}};};intmain(){Foo();Barbar;Bar::Pubpub;}Possible output:
Foo Bar Pub ~Bar (since C++11)Function contract specifiers
Function declarations and
can contain a sequence of function contract specifiers , each specifier has the following syntax:
preattr(optional)(predicate)(1) postattr(optional)(predicate)(2) postattr(optional)(identifierresult-attr(optional):predicate)(3) 1) Introduces a precondition assertion .
2,3) Introduces a postcondition assertion .
2) The assertion does not bind to the result.
3) The assertion binds to the result.
attr- a list of attributes appertaining to the introduced contract assertion predicate- any expression (except unparenthesized
) identifier- the identifier that refers to the result result-attr- a list of attributes appertaining to the result binding
Precondition assertion and postcondition assertion are collectively called function contract assertion .
A function contract assertion is a
associated with a function. The predicate of a function contract assertion is its predicate
to bool.
The following functions cannot be declared with function contract specifiers:
function
on their first declarations
Precondition assertions
A precondition assertion is associated with entering a function:
intdivide(intdividend,intdivisor)pre(divisor!=0){returndividend/divisor;}doublesquare_root(doublenum)pre(num>=0){returnstd::sqrt(num);}Postcondition assertions
A postcondition assertion is associated with exiting a function normally.
If a postcondition assertion has an identifier , the function contract specifier introduces identifier as the name of a result binding of the associated function. A result binding denotes the object or reference returned by invocation of that function. The type of a result binding is the return type of its associated function.
intabsolute_value(intnum)post(r:r>=0){returnstd::abs(num);}doublesine(doublenum)post(r:r>=-1.0&&r<=1.0){if(std::isnan(num)||std::isinf(num))// exiting via an exception never causes contract violationthrowstd::invalid_argument("Invalid argument");returnstd::sin(num);}If a postcondition assertion has an identifier , and the return type of the associated function is (possibly cv-qualified) void, the program is ill-formed:
voidf()post(r:r>0);// Error: no value can be bound to “r”When the declared return type of a non-templated function contains a
, a postcondition assertion with an identifier can only appear in a function definition:
autog(auto&)post(r:r>=0);// OK, “g” is a templateautoh()post(r:r>=0);// Error: cannot name the return valueautok()post(r:r>=0)// OK, “k” is a definition{return0;}Contract consistency
A
D of a function or function template func must have either no contract-specs or the same contract-specs as any first declaration F reachable from D. If D and F are in different translation units, a diagnostic is required only if D is attached to a named module.
If a declaration F1 is a first declaration of func in one translation unit and a declaration F2 is a first declaration of func in another translation unit, F1 and F2 must specify the same contract-specs , no diagnostic required.
Two contract-specs s are the same if they consist of the same function contract specifiers in the same order.
A function contract specifier C1 on a function declaration D1 is the same as a function contract specifier C2 on a function declaration D2 if all following conditions are satisfied:
The predicate s of C1 and C2 would satisfy the
if placed in function definitions on the declarations D1 and D2 (if D1 and D2 are in different translation units, corresponding entities defined within each predicate behave as if there is a single entity with a single definition), respectively, except for the following renamings: The renaming of the parameters of the declared function.
The renaming of template parameters of a template enclosing the declared function.
The renaming of the result binding (if any).
Both C1 and C2 have an identifier or neither have.
If this condition is not met solely due to the comparison of two lambda expressions that are contained within the predicate s, no diagnostic is required.
boolb1,b2;voidf()pre(b1)pre([]{returnb2;}());voidf();// OK, function contract specifiers omittedvoidf()pre(b1)pre([]{returnb2;}());// Error: closures have different typesvoidf()pre(b1);// Error: function contract specifiers are differentintg()post(r:b1);intg()post(b1);// Error: no result bindingnamespaceN{voidh()pre(b1);boolb1;voidh()pre(b1);// Error: function contract specifiers differ// according to the one−definition rule}(since C++26)Notes
In case of ambiguity between a variable declaration using the direct-initialization syntax and a function declaration, the compiler always chooses function declaration; see
.
Feature-test macro ValueStdFeature
(C++14)
(C++14)
for normal functions
(C++23)
(
)
(C++26)deleted function with a reason Keywords
,
,
,
Example
Run this code
#include<iostream>#include<string>// simple function with a default argument, returning nothingvoidf0(conststd::string&arg="world!"){std::cout<<"Hello, "<<arg<<'\n';}// the declaration is in namespace (file) scope// (the definition is provided later)intf1();// function returning a pointer to f0, pre-C++11 stylevoid(*fp03())(conststd::string&){returnf0;}// function returning a pointer to f0, with C++11 trailing return typeautofp11()->void(*)(conststd::string&){returnf0;}intmain(){f0();fp03()("test!");fp11()("again!");intf2(std::string)noexcept;// declaration in function scopestd::cout<<"f2(\"bad\"): "<<f2("bad")<<'\n';std::cout<<"f2(\"42\"): "<<f2("42")<<'\n';}// simple non-member function returning intintf1(){return007;}// function with an exception specification and a function try blockintf2(std::stringstr)noexcepttry{returnstd::stoi(str);}catch(conststd::exception&e){std::cerr<<"stoi() failed!\n";return0;}// deleted function, an attempt to call it results in a compilation errorvoidbar()=delete# if __cpp_deleted_function("reason")# endif;Possible output:
stoi() failed! Hello, world! Hello, test! Hello, again! f2("bad"): 0 f2("42"): 42 Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR Applied to Behavior as published Correct behavior
C++98 member functions defined in class
could not have a parameter of or return
its own class because it is incomplete allowed
C++98 a parameter could have cv-qualified void type prohibited
C++98 types that include pointers/references to
array of unknown bound could not be parameters such types are allowed
C++98 member initializer list was not a part of function body it is
C++98 dependent type void could be used to
declare a function taking no parameters only non-dependent
void is allowed
C++11 defaulted or deleted functions could not
be specified with override or finalallowed
C++11 only special member functions could be user-provided extended to all functions
C++11 deleted functions could not have any parameter of
an incomplete type or return an incomplete type incomplete type allowed
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 check in the
context of the
function definition
C++14 return type deduction treated return; as return void();simply deduce the return
type as void in this case
C++11 the implicit odr-use of a deleted
virtual function was ill-formed such odr-uses are exempt
from the use prohibition
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
C++14 function redeclarations could use return type
deduction even if the initial declaration does not not allowed
C++11 {} could be a function body or an initializer at the same place differentiated by the type
of the declarator identifier
C++98 the declarator in function definition could not be parenthesized allowed
C++11 the ambiguity resolution rule regarding parenthesized
type names did not cover lambda expressions covered
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
only check in the
function body
C++98 the function body of a constructor did not include the initializations
not specified in the constructor's regular function body also includes these
initializations
C++20 a function definition with a requires-clause
could define a non-templated function prohibited
C++23 explicit object member functions could not have out-of-class definitions allowed
C++23 unnamed explicit object parameters could have type voidprohibited See also