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

2025-11-07


2900. Deduction of non-type template arguments with placeholder types

Section: 13.10.3.6  [temp.deduct.type]     Status: ready     Submitter: Hubert Tong     Date: 2024-06-05

(From submission #546.)

The deduction rule for non-type template parameters in 13.10.3.6 [temp.deduct.type] paragraph 20 seems lacking:

If P has a form that contains <i>, and if the type of i differs from the type of the corresponding template parameter of the template named by the enclosing simple-template-id, deduction fails. If P has a form that contains [i], and if the type of i is not an integral type, deduction fails. [ Footnote: ... ] ...

This wording does not address the situation when the declared type of i is a placeholder type, or when the type of the corresponding template parameter of the template named by the enclosing simple-template-id is a placeholder type.

Proposed resolution (approved by CWG 2025-11-06):

  1. Change in 9.2.9.7.2 [dcl.type.auto.deduct] paragraph 3 as follows:

    If the placeholder-type-specifier is of the form type-constraint opt auto, the deduced type T' replacing T is determined using the rules for template argument deduction. If the initialization is copy-list-initialization, a declaration of std::initializer_list shall precede (6.5.1 [basic.lookup.general]) the placeholder-type-specifier . Obtain P from T by replacing the occurrence of type-constraint opt auto either with a new invented type template parameter U or, if the initialization is copy-list-initialization, with std::initializer_list<U>. If E is a value synthesized for a constant template parameter of type decltype(auto) (13.7.7.3 [temp.func.order]), the declaration is ill-formed. Otherwise, deduce Deduce a value for U using the rules of template argument deduction from a function call (13.10.3.2 [temp.deduct.call]), where P is a function template parameter type and the corresponding argument is E. If the deduction fails, the declaration is ill-formed. Otherwise, T' is obtained by substituting the deduced U into P.
  2. Change in 13.7.7.3 [temp.func.order] paragraph 3 as follows:

    To produce the transformed template, for each type, constant, type template, variable template, or concept template parameter (including template parameter packs (13.7.4 [temp.variadic]) thereof) synthesize a unique type, value, class template, variable template, or concept, respectively, and substitute it for each occurrence of that parameter in the function type of the template.

    [Note 1: The type replacing the a placeholder in the type of the value synthesized for a constant template parameter is also a unique synthesized type. end note]

  3. Change in 13.10.3.6 [temp.deduct.type] paragraph 20 as follows:

    If P has a form that contains <i>, and if the type of i differs from the type deduction fails unless the type of i is the same as that of the corresponding template parameter p in the specialization (from A) of the template named by the enclosing simple-template-id, deduction fails ; if the declared type of i contains a placeholder type, the corresponding template argument for the purposes of placeholder type deduction (9.2.9.7.2 [dcl.type.auto.deduct]) is an id-expression for p. If P has a form that contains [i], and if the type of i is not an integral type, deduction fails. [ Footnote: ... ] ... [ Example 13:
      template<int i> class A { /* ... */ };
      template<short s> void f(A<s>);
      void k1() {
        A<1> a;
        f(a);      // error: deduction fails for conversion from int to short
        f<1>(a);   // OK
      }
      template<const short cs> class B { };
      template<short s> void g(B<s>);
      void k2() {
        B<1> b;
        g(b);  // OK, cv-qualifiers are ignored on template parameter types
      }
    

    template<auto> struct C; template<long long x> void f(C<x> *); void g(C<0LL> *ap) { f(ap); // OK, deduces long long value from 0LL } template<int> struct D; template<auto x> void f(D<x> *); void g(D<0LL> *ap) { f(ap); // OK, deduces x as an int value } template<int &> struct E; template<auto x> void f(E<x> *); int v; void g(E<v> *bp) { f(bp); // error: type int of x does not match the int & type of the template parameter in the E<v> specialization of E } template<const int &> struct F; template<decltype(auto) x> void f(F<x> *); int i; void g(F<i> *ap) { f(ap); // OK, deduces x as a constant template parameter of type const int & } template <decltype(auto) q> struct G; template <auto x> long *f(G<x> *); // #1 template <decltype(auto) x> short *f(G<x> *); // #2 const int j = 0; short *g(G<(j)> *ap) { // OK, q has type const int & return f(ap); // OK, only #2 matches } long *g(G<j> *ap) { // OK, q has type int return f(ap); // OK, #1 is more specialized }

    -- end example]