Drafting note: This also resolves core issues 1001, 1322, and 1444. The roadmap is as follows: Parameter types that are dependent are not adjusted for declaration matching. For deduction, full adjustments are done (so array bounds or top-level const cannot be deduced). Then, we substitute into the original declaration. The notion of "functionally equivalent, but not equivalent" is extended to all dependent parameter types, beyond non-type template parameters. When instantiating a non-template member function of a class template, the member function becomes an ordinary function for purposes of overload resolution. We should substitute into the original declarators of the parameters.
Change in 3.5 basic.link paragraph 9:Change in 7.3.3 namespace.udecl paragraph 14:
- ...
when both names denote functions, the parameter-type-lists of the functions (8.3.5) are identical; and- when both names denote functions or function templates, the signatures (8.3.5 dcl.fct, 14.5.6.1 temp.over.link) are the same.
If a function or function template declaration in namespace scope or block scope has the sameChange in 7.3.3 namespace.udecl paragraph 15:name and the same parameter-type-list (8.3.5)signature (8.3.5 dcl.fct, 14.5.6.1 temp.over.link) as a function or function template introduced by a using-declaration, and the declarations do not declare the same function or function template, the program is ill-formed.If a function template declaration in namespace scope has the same name, parameter-type-list, return type, and template parameter list as a function template introduced by a using-declaration, the program is ill-formed.[ Note: ... ]
When a using-declaration brings names from a base class into a derived class scope, member functions and member function templates in the derived class override and/or hide member functions and member function templates with the samename, parameter-type-list (8.3.5), cv-qualification, and ref-qualifier (if any)signature (8.3.5 dcl.fct, 14.5.6.1 temp.over.link) in a base class (rather than conflicting). [ Note: ... ]
Drafting note: The above changes the semantics for member function templates to also consider return types and the template parameter list. Implementations vary in their status-quo treatment; see c++std-core-24880.
Change in 8.3.5 dcl.fct paragraph 1:... the type of the declarator-id in D is "derived-declarator-type-list function of (Change in 8.3.5 dcl.fct paragraph 2:parameter-declaration-clauseparameter-type-list ) cv-qualifier-seqopt ref-qualifieropt returning T". ...
... The type of the declarator-id in D is "derived-declarator-type-list function of (Change in 8.3.5 dcl.fct paragraph 5 and split it:parameter-declaration-clauseparameter-type-list) cv-qualifier-seqopt ref-qualifieropt returning trailing-return-type". ...
Change in 10.3 paragraph 2:A single name can be used for several different functions in a single scope; this is function overloading (Clause 13). 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 parameter-type-list is determined as follows: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. [ Example: A parameter declared as "
- The declared type of each parameter (including function parameter packs) is determined from its own decl-specifier-seq and declarator or abstract-declarator.
- For each non-dependent parameter type, the following adjustments and transformations are applied:
After determining the type of each parameter, anyA parameter of declared type "array of T" or "function returning T" is adjusted to be of type "pointer to T" or "pointer to function returning T," respectively.After producing the list of parameter types,A transformed parameter type is produced from the adjusted parameter type by deleting any top-level cv-qualifiersmodifying a parameter type are deleted when forming the function type.- For a dependent parameter type, the transformed type is identical to the declared type.
void()(const int, int[5])
" is transformed to "void(*)(int,int*)
". -- end example ] [ Note:This transformationThe removal of top-level cv-qualifiers does not affect the types of theparametersfunction parameter variables. For example,int(*)(const int p, decltype(p)*)
andint(*)(int, const int*)
are identical types. -- end note ]A single name can be used for several different functions in a single scope; this is function overloading (Clause 13). All declarations for a function shall agree exactly in both the return type and the parameter-type-list.
If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the sameChange in 13.1 over.load paragraph 2:name, parameter-type-list (8.3.5), cv-qualification, and ref-qualifier (or absence of same)signature (1.3.20 defns.signature.member) as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared) and it overrides [ Footnote ... ] Base::vf.
Certain function and function template declarations cannot be overloaded:Change in 13.1 over.load paragraph 3:
- ...
Change in 13.2 over.dcl paragraph 1:
ParameterNon-dependent parameter declarations that differ only in a pointer * versus an array [] are equivalent. ...ParameterNon-dependent parameter declarations that differ only in that one is a function type and the other is a pointer to the same function type are equivalent. ...ParameterNon-dependent parameter declarations that differ only in the presence or absence of const and/or volatile are equivalent. ...
Two function or function template declarationsChange in 14.1 temp.param paragraph 8:of the same namerefer to the same function or function template if theyare in the same scope and have equivalent parameter declarations (13.1 over.load)have the same signature (1.3 intro.defs). For purposes of signature comparison, a function declared as a member of a base class is not considered to be a member of a derived classA function member of a derived class is not in the same scope as a function member of the same name in a base class. [ Example: ...]
A non-type template-parameter of non-dependent type "array of T" or "function returning T" is adjusted to be of type "pointer to T" or "pointer to function returning T", respectively. [ Example:Change in 14.5.6.1 temp.over.link paragraph 3:template<int* a> struct R { / ... / }; template<int b[5]> struct S { / ... / }; int p; R<&p> w; // OK S<&p> x; // OK due to parameter adjustment int v[5]; R<v> y; // OK due to implicit argument conversion S<v> z; // OK due to both adjustment and conversion template<typename T, T[1]> struct A; template<typename T, T*> struct A; // error: not a redeclaration template<typename T, int[T::size]> struct B {}; int x; B<int, &x> b; // error: type "int" has no member called "size"-- end example ]
The signature of a function template is defined in 1.3 intro.defs, where the syntactic form of the return type and the types in the parameter type list is significant if they are dependent, as described below for the equivalence of dependent types.Turn 14.5.6.1 temp.over.link paragraph 4 into a note:The names of the template parameters are significant only for establishing the relationship between the template parameters and the rest of the signature.[ Note: ... ]
[ Note: When an expression that references a template parameter ... [ Example: ... -- end example ]Change in 14.5.6.1 temp.over.link paragraph 5:[ Note: Most expressions that use template parameters use non-type template parameters, but it is possible for an expression to reference a type parameter. For example, a template type parameter can be used in the sizeof operator.-- end note ]
TwoChange in 14.5.6.1 temp.over.link paragraph 6:expressions involving template parameters are considereddependent types are equivalent if two function definitionscontaining the expressionsusing the types to declare an invented variable would satisfy the one definition rule (3.2 basic.def.odr), except that the tokens used to name the template parameters may differ as long as a token used to name a template parameter in oneexpressiondefinition is replaced by another token that names the same template parameter in the otherexpressiondefinition. For determining whether two dependent names (14.6.2 temp.dep) are equivalent, only the name itself is considered, not the result of name lookup in the context of the template. If multiple declarations of the same function template differ in the result of this name lookup, the result for the first declaration is used. [ Example:template <int I, int J> void f(A<I+J>); // #1 template <int K, int L> void f(A<K+L>); // same as #1 template <class T> decltype(g(T())) h(); int g(int); template <class T> decltype(g(T())) h() // redeclaration of h() uses the earlier lookup { return g(T()); } // ...although the lookup here does find g(int) int i = h<int>(); // template argument substitution fails; g(int) // was not in scope at the first declaration of h()-- end example ] Twoexpressions involving template parametersdependent types that are not equivalent are functionally equivalent if, for any given set of template arguments,the evaluation of the expression results in the same valuesubstitution (14.8.2 temp.deduct) results in either the same type or substitution failure for both types. [ Example: For a type template parameterT
, the dependent typesdecltype(T())
anddecltype((T()))
are functionally equivalent, but not equivalent. Similarly, for a non-type template parameterI
of typeint
, the dependent typesA<I+I>
andA<I + I>
are equivalent, and each is functionally equivalent (but not equivalent) toA<2*I>
. -- end example ]
Change in 14.8.2 temp.deduct paragraph 3:Two function templates are equivalent if they are declared in the same scope, have the same name, have identical template parameter lists, and have return types and parameter lists that are equivalent using the rules described above to compare expressions involving template parameters.Two function templates are functionally equivalent if theyare equivalenthave the same signature except that one or moreexpressions that involve template parameters in the return types and parameter listsof the return type and the types in the parameter type list are functionally equivalent using the rules described aboveto compare expressions involving template parameters. If a program contains declarations of function templates that are functionally equivalent butnot equivalentdo not have the same signature, the program is ill-formed; no diagnostic is required.
After this substitution is performed, an intermediate function type is determined by applying the function parameter type adjustments and transformations described in 8.3.5 dcl.fctare performed, even to dependent parameter types. [ Example: For a declarationtemplate<class R, class T, int n> R f(const T[n]);
, the intermediate function type for the callf<int>(nullptr)
isint f(T*)
. -- end example ][ Example: A parameter type of "[ Example: ... ]void ()(const int, int[5])
" becomes "void(*)(int,int*)
". -- end example ] [ Note: A top-level qualifier in a function parameter declaration does not affect the function type but still affects the type of the function parameter variable within the function. -- end note ]
Join 14.8.2 temp.deduct paragraph 4 (consisting of a note relating to the preceding example) with the preceding paragraph 3.
Change in 14.8.2 temp.deduct paragraph 5:Theresulting substituted and adjustedintermediate function type so determined is used as the type of the function template for template argument deduction. ... [ Example: ... ] When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in the template parameter list of the template and the function type are replaced with the corresponding deduced or default argument values. After replacement, adjustments and transformations are applied to the types of non-type template parameters as described in 14.1 temp.param and to the function parameter types as described in 8.3.5 dcl.fct. If the substitution results in an invalid type, as describedabovebelow, type deduction fails. [ Example:template<class T> struct B { typedef int A[2]; virtual void f(const A); }; template<class T> struct D : B<T> { virtual void f(const typename B<T>::A) override; }; D<int> d; // ok[ Note: The dependent type ofD::f
's parameter is not adjusted or transformed when parsing the class template definition. During instantiation ofD<int>
(14.7.1 temp.inst), substitution intoD::f
's function type yields the parameter typeconst B<int>::A
, which is adjusted and transformed to type "pointer to const int". -- end note ] -- end example ]
Drafting note: The function type contains unadjusted, non-transformed dependent parameter types (see definition of parameter-type-list in 8.3.5), so this substitutes into the original declaration, as desired.