Document number:  00-0008/N1231
Date:  March 7, 2000
Author:  John Spicer, Edison Design Group
 jhs@edg.com

Miscellaneous Template Issues for Tokyo Meeting

Introduction

This document discusses several issues that I volunteered to work on between meetings:

  1. Syntactic disambiguation using the template keyword (issue 96)
  2. Are classes nested in templates dependent?
  3. Definition of dependent name (possibly new)

96. Syntactic disambiguation using the template keyword.

This is a revision to issue 96 in the core issues list. As described below, experience with template template parameters has resulted in a change in my recommendation with respect to this issue.

In my original reflector posting on this subject I recommended that the standard be changed to require that the template keyword be followed by a template-id (i.e., a name followed by a template-argument-list).

The following example gives the rationale for this recommendation. The template keyword is only supposed to provide syntactic disambiguation, not affect name lookup (just as is the case with the typename keyword). This results in the surprising behavior that call #2 does not call the template function.

struct A { void f(int); template <class T> void f(T); }; template <class T> void f(T t) { A a; a.template f<>(t); // #1 calls template a.template f(t); // #2 but does this call the template? } The counter example came up while we were implementing template template parameters. In this example, the template keyword is needed in the default argument to indicate that T::C is a template, just as typename would be required if T::C were a type. template <class T> struct A {template <class T2> struct C {}; }; template <class T, template <class X> class TT = T::template C> struct B {}; In other words, there are cases where we need to permit the template keyword without a following template-id.

If we allow the template keyword to be followed by a name without a template-argument-list we must then decide what it means for functions in such cases (i.e., we must resolve the issue illustrated by the first example above). For classes it is not an issue (if the name is followed by a template-argument-list, it refers to a specialization of the class template, if not, it refers to the class template itself).

There are two possible interpretations that I can think of:

  1. When the template keyword is applied to an overload set containing both template and non-templates, the non-templates are ignored. In other words, it is treated as if the name were followed by an empty template-argument-list (i.e., <>).
  2. If the template keyword is followed by a name that does not have a template-argument-list, the program is ill-formed if the name refers to anything except a class template (i.e., it cannot refer to a function, function template, or overload set).
Note that a name that refers to a class template (and not to a specialization of the class template) must be a template argument associated with a template template parameter, because there is no other context in which a template name without a template-argument-list is permitted).

The first option has the benefit of being (presumably) what people would find most natural. The second has the benefit of retaining the property that the template keyword is for syntactic guidance without semantic implications.

Are classes nested in templates dependent?

This is the proposed wording change for core issue 108.

Add after 14.6.1 paragraph 2:

Within the scope of a class template, when the unqualified name of a nested class of the class template is referred to, it is equivalent to the name of enclosing class template followed by the name of the nested class. [Example: template <class T> struct A { class B {}; // B is equivalent to A::B, which is equivalent to A<T>::B // which is dependent. class C : B { }; }; --end example]

Definition of dependent name

This issue was discussed on the reflector and may already be in the updated core issues list.

There is a problem with the dependency rules when referring to members of the "current instantiation" of a class template. The example below (a modified version of Derek Inglis' example from core-8387) illustrates that the name used to refer to an entity can render the same entity either dependent or non-dependent.

I believe this behavior is surprising, and that programmers would expect that whether you refer to the members as A, a, and a2 or S::A, S::a and S::a2 or S<T>::A, S<T>::a and S<T>::a2, the behavior should be the same.

Note that the behavior should only be the same when you refer to the template-id that is equivalent to the unqualified template name as described in 14.6.1p1. Something like S<T*>::A would not be equivalent to S::A or A.

void foo(char); template <class T> struct S { typedef int A; int a; A a2; S::A a3; void bar() { foo( S<T>::A() ); // #1 foo( A() ); // #2 foo( S<T>::a ); // #3 foo( a ); // #4 foo( a2 ); // #5 foo( a3 ); // #6 } }; void foo(int); int main() { S<int> s; s.bar(); } But according to the standard, these are not actually equivalent.

S<T>::a is dependent because the id-expression contains a template-id that is dependent. Likewise, S::a is dependent because it is defined as being equivalent to S<T>::a. As far as I can tell, plain a is not dependent while this->a is. If plain a is not dependent then neither is plain A, making a2 also non-dependent. a3 is dependent though, because its type was declared using a dependent syntax.

I don't think these rules are reasonable.

An id-expression should be dependent if its type is dependent. The form of the identifier reference should not make a difference. Similarly, the type of a, a2, and a3 should be known to be int, and none of them should be considered dependent.

The standard would need to be clarified to say that within the scope of S you can look up things like S::A or S<T>::A to find out the type denoted by A (note that this does not apply to looking up something like S<T*>::A).

End of document.