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-21


2862. Unclear boundaries of template declarations

Section: 13.1  [temp.pre]     Status: review     Submitter: Jan Schultke     Date: 2024-02-21

(From submission #506.)

Consider:

  template <int>
  struct S {} v;

Is this a declaration of a class template or a declaration of a variable template, or neither? Implementations uniformly reject.

Another example:

  template<class T>
  typedef struct C F; 

Proposed resolution (approved by CWG 2024-04-05) [SUPERSEDED]:

Change in 13.1 [temp.pre] paragraph 2 through 5 as follows:

The declaration in a template-declaration (if any) shall

The declaration shall not be an export-declaration or a simple-declaration whose decl-specifier-seq contains typedef.

A template-declaration is a declaration. If

the template-declaration declares a member of C, and the nested-name-specifier is treated as a non-dependent reference to C for the purpose of further interpreting the declaration. Otherwise: [ Example:
  template<typename T> struct A {
    template<typename U> struct B;
    template<typename U> struct B<U*> {
      template<typename V> void f();
    };
  };
  template<typename T> // #1
  template<typename U> // #2
  template<typename V> // #3
  void A<T>::B<U*>::f() {}
The template-declaration #1 declares a member of the class template A, because A<T> is equivalent to the injected-class-name of A. The template-declaration #2 declares a member of the class template partial specialization A<T>::B<U*>, because A<T>::B<U*> is equivalent to the injected-class-name of the partial specialization when A<T> is treated as a non-dependent reference to the primary template A. The template-declaration #3 declares a function template that is a member of the class template partial specialization. -- end example ] A declaration introduced by a template declaration of a variable is a variable template. A variable template at class scope is a static data member template. [ Example: ... ]

[Note 2: A template-declaration can appear only as a namespace scope or class scope declaration. —end note] Its declaration shall not be an export-declaration. A template-declaration shall declare exactly one template or member of a template. [ Example:

template <int>
struct S {} v;    // error: declares both a class template and a variable template
template <int>
struct V *v;      // OK: declares a variable template
-- end example ] In a function template declaration, the unqualified-id of the declarator-id shall be a name. [Note 3: A class or variable template declaration of a simple-template-id declares a partial specialization (13.7.6 [temp.spec.partial]). —end note]

In a template-declaration, explicit specialization, or explicit instantiation, the init-declarator-list in the declaration shall contain at most one declarator. [ Note: When such a declaration is used to declare a class template, no declarator is permitted, because the declarator would be considered to declare a variable or function template in addition to the class template. -- end note ]

CWG 2024-06-26

This drafting lacks the required positional association of template-heads with the respective component of the nested-name-specifier.

Possible resolution (rebased on the current Working Draft) [SUPERSEDED]:

Change in 13.1 [temp.pre] paragraph 2 through 5 as follows:

The declaration in a template-declaration (if any) shall

The declaration shall not be an export-declaration or a simple-declaration whose decl-specifier-seq contains typedef.

A template-declaration is a declaration. If

the template-declaration declares a member of C, and the nested-name-specifier is treated as a non-dependent reference to C for the purpose of further interpreting the declaration. Otherwise: [ Example:
  template<typename T> struct A {
    template<typename U> struct B;
    template<typename U> struct B<U*> {
      template<typename V> void f();
    };
  };
  template<typename T> // #1
  template<typename U> // #2
  template<typename V> // #3
  void A<T>::B<U*>::f() {}
The template-declaration #1 declares a member of the class template A, because A<T> is equivalent to the injected-class-name of A. The template-declaration #2 declares a member of the class template partial specialization A<T>::B<U*>, because A<T>::B<U*> is equivalent to the injected-class-name of the partial specialization when A<T> is treated as a non-dependent reference to the primary template A. The template-declaration #3 declares a function template that is a member of the class template partial specialization. -- end example ] A declaration introduced by a template declaration of a variable is a variable template. A variable template at class scope is a static data member template. [ Example: ... ]

[Note 2: A template-declaration can appear only as a namespace scope or class scope declaration. —end note] Its declaration shall not be an export-declaration. A template-declaration shall declare exactly one templated entity (not considering declarations inhabiting scopes nested therein). [ Example:

template <int>
struct S {} v;    // error: declares both a class template and a variable template
template <int>
struct V *v;      // OK, declares a variable template
-- end example ] In a function template declaration, the unqualified-id of the declarator-id shall be a name. [Note 3: A class or variable template declaration of a simple-template-id declares a partial specialization (13.7.6 [temp.spec.partial]). —end note]

In a template-declaration, explicit specialization, or explicit instantiation, the init-declarator-list in the declaration shall contain at most one declarator. [ Note: When such a declaration is used to declare a class template, no declarator is permitted, because the declarator would be considered to declare a variable or function template in addition to the class template. -- end note ]

Additional notes (December, 2024)

Subclause 7.5.5.3 [expr.prim.id.qual] paragraph 3 already interprets declarative nested-name-specifiers.

Possible resolution:

Change in 13.1 [temp.pre] paragraph 2 through 5 as follows:

The declaration in a template-declaration (if any) shall

The declaration shall not be an export-declaration or a simple-declaration whose decl-specifier-seq contains typedef.

A template-declaration is a declaration. If the declaration in a template-declaration introduces an entity using a declarative nested-name-specifier that nominates a primary class template or class template partial specialization C (7.5.5.3 [expr.prim.id.qual]), the template-declaration declares a member of C, and the nested-name-specifier is treated as a non-dependent reference to C for the purpose of further interpreting the declaration. Otherwise:

[ Example:
  template<typename T> struct A {
    template<typename U> struct B;
    template<typename U> struct B<U*> {
      template<typename V> void f();
    };
  };
  template<typename T> // #1
  template<typename U> // #2
  template<typename V> // #3
  void A<T>::B<U*>::f() {}
The template-declaration #1 declares a member of the class template A, because A<T> is equivalent to the injected-class-name of A. The template-declaration #2 declares a member of the class template partial specialization A<T>::B<U*>, because A<T>::B<U*> is equivalent to the injected-class-name of the partial specialization when A<T> is treated as a non-dependent reference to the primary template A. The template-declaration #3 declares a function template that is a member of the class template partial specialization. -- end example ] A declaration introduced by a template declaration of a variable is a variable template. A variable template at class scope is a static data member template. [ Example: ... ]

[Note 2: A template-declaration can appear only as a namespace scope or class scope declaration. —end note] Its declaration shall not be an export-declaration. A template-declaration shall declare exactly one templated entity (not considering declarations inhabiting scopes nested therein). [ Example:

template <int>
struct S {} v;    // error: declares both a class template and a variable template
template <int>
struct V *v;      // OK, declares a variable template
-- end example ] In a function template declaration, the unqualified-id of the declarator-id shall be a name. [Note 3: A class or variable template declaration of a simple-template-id declares a partial specialization (13.7.6 [temp.spec.partial]). —end note]

In a template-declaration, explicit specialization, or explicit instantiation, the init-declarator-list in the declaration shall contain at most one declarator. [ Note: When such a declaration is used to declare a class template, no declarator is permitted, because the declarator would be considered to declare a variable or function template in addition to the class template. -- end note ]