Jens Maurer
2014-03-25

Core Issue 1668: Parameter type determination still not clear enough

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:
If a function or function template declaration in namespace scope or block scope has the same 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: ... ]
Change in 7.3.3 namespace.udecl paragraph 15:
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 same name, 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 (parameter-declaration-clause parameter-type-list ) cv-qualifier-seqopt ref-qualifieropt returning T". ...
Change in 8.3.5 dcl.fct paragraph 2:
... The type of the declarator-id in D is "derived-declarator-type-list function of (parameter-declaration-clause parameter-type-list) cv-qualifier-seqopt ref-qualifieropt returning trailing-return-type". ...
Change in 8.3.5 dcl.fct paragraph 5 and split it:
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 "void()(const int, int[5])" is transformed to "void(*)(int,int*)". -- end example ] [ Note: This transformation The removal of top-level cv-qualifiers does not affect the types of the parameters function parameter variables. For example, int(*)(const int p, decltype(p)*) and int(*)(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.

Change in 10.3 paragraph 2:
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 same 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.
Change in 13.1 over.load paragraph 2:
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:
Two function or function template declarations of the same name refer to the same function or function template if they are 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 class A 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: ...]
Change in 14.1 temp.param paragraph 8:
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:
  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 ]
Change in 14.5.6.1 temp.over.link paragraph 3:
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. The names of the template parameters are significant only for establishing the relationship between the template parameters and the rest of the signature. [ Note: ... ]
Turn 14.5.6.1 temp.over.link paragraph 4 into a note:
[ Note: When an expression that references a template parameter ... [ Example: ... -- end example ] [ 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 ]
Change in 14.5.6.1 temp.over.link paragraph 5:
Two expressions involving template parameters are considered dependent types are equivalent if two function definitions containing the expressions using 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 one expressiondefinition is replaced by another token that names the same template parameter in the other expression definition. 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 ] Two expressions involving template parameters dependent 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 value substitution (14.8.2 temp.deduct) results in either the same type or substitution failure for both types. [ Example: For a type template parameter T, the dependent types decltype(T()) and decltype((T())) are functionally equivalent, but not equivalent. Similarly, for a non-type template parameter I of type int, the dependent types A<I+I> and A<I + I> are equivalent, and each is functionally equivalent (but not equivalent) to A<2*I>. -- end example ]
Change in 14.5.6.1 temp.over.link paragraph 6:
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 they are equivalent have the same signature except that one or more expressions that involve template parameters in the return types and parameter lists of the return type and the types in the parameter type list are functionally equivalent using the rules described above to compare expressions involving template parameters. If a program contains declarations of function templates that are functionally equivalent but not equivalent do not have the same signature, the program is ill-formed; no diagnostic is required.
Change in 14.8.2 temp.deduct paragraph 3:
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.fct are performed, even to dependent parameter types. [ Example: For a declaration template<class R, class T, int n> R f(const T[n]);, the intermediate function type for the call f<int>(nullptr) is int f(T*). -- end example ] [ Example: A parameter type of "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 ] [ Example: ... ]

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:
The resulting substituted and adjusted intermediate 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 described above below, 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 of D::f's parameter is not adjusted or transformed when parsing the class template definition. During instantiation of D<int> (14.7.1 temp.inst), substitution into D::f's function type yields the parameter type const 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.