Title
Template argument disambiguation
Status
open
Section
13.4.1 [temp.arg.general]
Submitter
Hubert Tong

Created on 2025-02-03.00:00:00 last changed 5 months ago

Messages

Date: 2025-07-04.06:38:50

(From submission #669.)

For the template-argument grammar in 13.3 [temp.names], there is a parsing ambiguity introduced by P2841R7 between nested-name-specifieropt template-name and type-id (via simple-type-specifier) naming a placeholder for deduced class type (9.2.9.8 [dcl.type.class.deduct]). For example:

  void g();

  template <typename T>
  struct A {
    static void f() {
      g<T::TT>((A *)0);
    }
  };

  template <typename T> void g(void *);
  template <auto> void g(void *);
  template <template <typename> class> void g(void *);

  struct Expr { enum { TT = 0 }; };
  struct Type { using TT = int; };
  struct Tmpl { template <typename> struct TT; };
  void h() {
    A<Expr>::f(); // all accept
    A<Type>::f(); // EDG, MSVC accept; GCC, Clang rejects
    A<Tmpl>::f(); // EDG, MSVC accept; GCC, Clang rejects
  }

P1787R6 established the direction that the template disambiguator is needed only when introducing a template-argument-list (13.3 [temp.names] paragraph 6). (Reflection made this assumption false.) All examples should be accepted.

See 13.8.2 [temp.local] for the use of an injected-class-name as a template argument.

Example:

  void g();

  template <typename T>
  struct A {
    static void f() {
      g<A>((A *)0);     // all accept
      g<A>((A *)0, 0);  // Clang, GCC, EDG accept; MSVC rejects
    }
  };

  template <typename T> void g(void *);
  template <template <typename> class> void g(void *, int);

  void h() { A<int>::f(); }

Possible resolution:

Strategy: Introduce a new grammar production akin reflection-name, as a preferred option for template-argument, and say what it means in the various cases.

  1. Change in 13.3 [temp.names] paragraph 1 as follows:

    template-argument:
       template-argument-name
       constant-expression
       type-id
       nested-name-specifieropt template-name
       nested-name-specifier template template-name
    
    template-argument-name:
       nested-name-specifieropt identifier
       nested-name-specifier template identifier
    
  2. Insert a new paragraph before 13.3 [temp.names] paragraph 7 as follows:

    The component names of a template-argument-name are those of its nested-name-specifier (if any) and its identifier. The terminal name of a template-argument-name of the form nested-name-specifier template identifier shall denote a template.

    A template-id is valid if ...

  3. Insert a new paragraph before 13.4.1 [temp.arg.general] paragraph 3 as follows:

    If a template-argument A matches the form template-argument-name, it is interpreted as such; the identifier is looked up and its meaning is determined as follows:

    • If lookup finds an injected-class-name (13.8.2 [temp.local]), then:
      • If A is for a type template template parameter, A denotes the corresponding class template.
      • Otherwise, it denotes a type-name.
    • Otherwise, if lookup finds a template, A denotes that template.
    • Otherwise, if lookup finds a type alias or a type, A denotes the underlying type and is interpreted as a type-id.
    • Otherwise, A shall be a constant-expression.

    In a template-argument, an ambiguity between a type-id and an expression is resolved to a type-id, regardless of the form of the corresponding template-parameter...
History
Date User Action Args
2025-02-03 00:00:00admincreate