This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 116a. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-12-20


1001. Parameter type adjustment in dependent parameter types

Section: 9.3.4.6  [dcl.fct]     Status: review     Submitter: Jason Merrill     Date: 2009-11-08

According to 9.3.4.6 [dcl.fct] paragraph 5, top-level cv-qualifiers on parameter types are deleted when determining the function type. It is not clear how or whether this adjustment should be applied to parameters of function templates when the parameter has a dependent type, however. For example:

    template<class T> struct A {
       typedef T arr[3];
    };

    template<class T> void f(const typename A<T>::arr) { } // #1

    template void f<int>(const A<int>::arr);

    template <class T> struct B {
       void g(T);
    };

    template <class T> void B<T>::g(const T) { } // #2

If the const in #1 is dropped, f<int> has a parameter type of A* rather than the const A* specified in the explicit instantiation. If the const in #2 is not dropped, we fail to match the definition of B::g to its declaration.

Rationale (November, 2010):

The CWG agreed that this behavior is intrinsic to the different ways cv-qualification applies to array types and non-array types.

Notes, January, 2012:

Additional discussion of this issue arose regarding the following example:

    template<class T> struct A {
      typedef double Point[2];
      virtual double calculate(const Point point) const = 0;
    };

    template<class T> struct B : public A<T> {
      virtual double calculate(const typename A<T>::Point point) const {
        return point[0];
      }
    };

    int main() {
      B<int> b;
      return 0;
    }

The question is whether the member function in B<int> has the same type as that in A<int>: is the parameter-type-list instantiated directly (i.e., using the adjusted types) or regenerated from the individual parameter types?

(See also issue 1322.)

Additional notes (February, 2024)

There is implementation divergence for the following example (clang and EDG accept, gcc and MSVC reject):

  template<typename U>
  int f(const U t);

  int v;
  auto test = f(v);

  template<typename U>
  int f(U t) {
   static_assert(std::is_const<decltype(t)>::value, "t should be non-const");
   return 0;
  }

Possible resolution (option 1):

Change in 9.3.4.6 [dcl.fct] paragraph 4 as follows:

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 parameter-declaration (9.3 [dcl.decl]). 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 non-dependent 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. ...

Additional notes (December, 2024)

The resolution suggested above causes errors in ostensibly more common situations such as this one, requiring an Annex C entry:

  template <class T>
  void f(T*);                 // #1

  template <class T>
  void f(T* const) {}         // redeclaration of #2?

  int main() {
    f((int*)0);               // was ok, becomes ambiguous
  }

Possible resolution (option 2):

Change in 9.3.4.6 [dcl.fct] paragraph 4 as follows:

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 parameter-declaration (9.3 [dcl.decl]). 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”. If a function parameter type is adjusted from "array of T" and acquires the array type through a dependent type, and any top-level cv-qualifier modifies the dependent parameter type in any declaration of the templated function, the program is ill-formed; no diagnostic is required unless a cv-qualifier modifies the parameter type in the function definition. 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. ...

[ Example:

...
  void h(int x(const int));   // #3
  void h(int (*)(int)) {}     // defines #3

  template<class T>
  int i(T);                   // #4
  template<class T>
  int i(const T) {}           // defines #4
  int v = i<int[]>(0);        // error: cv-qualified array parameter type in definition of template

-- end example]