this->bar.baz<int>(); // error
The statement above, within the definition of template<typename T> Foo<T>::Foo()
, is well-formed, and should be accepted if C++11 mode or C++1y mode is enabled. But it was technically ill-formed according to C++03.
Both standards agree that this
is a type-dependent expression:
C++03 14.6.2.1/1; N3690 14.6.2.1/8:
A type is dependent if it is
[T
is a dependent type, and so is Foo<T>
.]
C++03/N3690 14.6.2.2/2:
this
is type-dependent if the class type of the enclosing member function is dependent.
[Since Foo<T>
is a dependent type, the expression this
in its member definition is type-dependent.]
Both standards begin 14.6.2.2 with:
Except as described below, an expression is type-dependent if any subexpression is type-dependent.
C++03 has only three simple categories of expressions with more exact descriptions:
Primary expressions (this
and looked-up names)
Expressions that specify their own type (like casts and new-expressions)
Expressions with constant type (like literals and sizeof
).
The first category is defined in C++03 14.6.2.2/3:
An id-expression is type-dependent if it contains:
an identifier that was declared with a dependent type,
a template-id that is dependent,
a conversion-function-id that specifies a dependent type,
a nested-name-specifier that contains a class-name that names a dependent type.
So the lone expression bar
is not dependent: it is an identifier and an id-expression, but none of the above apply.
But this->bar
is not an id-expression, or in any of the other C++03 exceptions, so we have to follow the subexpression rule. Since subexpression this
is type-dependent, the containing expression this->bar
is also type-dependent.
But in fact, as you noticed, the type of this->bar
can be known while parsing the template definition, without instantiating any template arguments. It is declared as a member of the primary template, so the name must bind to that member declaration. A template specialization might make Foo<T>::bar
undeclared or declared in a different way, but in that case the primary template won't be used at all and the current definition of Foo()
is ignored for that specialization. Which is why C++11 defined the concept of "the current instantiation" and used it for a further exception to the contagiousness of type-dependent expressions.
N3690 14.6.2.1/1:
A name refers to the current instantiation if it is
in the definition of a class template, a nested class of a class template, a member of a class template, or a member of a nested class of a class template, the injected-class-name of the class template or nested class
in the definition of a primary class template or a member of a primary class template, the name of the class template followed by the template argument list of the primary template (as described below) enclosed in <>
(or an equivalent template alias specialization),
...
[The first bullet says Foo
is the current instantiation. The second says Foo<T>
is the current instantiation. In this example, both name the same type.]
14.6.2.1/4:
A name is a member of the current instantiation if it is
An unqualified name that, when looked up, refers to at least one member of a class that is the current instantiation or a non-dependent base class thereof.
A qualified-id in which ...
An id-expression denoting the member in a class member access expression for which the type of the object expression is the current instantiation, and the id-expression, when looked up, refers to at least one member of a class that is the current instantiation or a non-dependent base class thereof.
[The first bullet says bar
alone is a member of the current instantiation. The third bullet says this->bar
is a member of the current instantiation.]
Finally, C++11 adds a fourth category of rules for type-dependent expressions, for member access. 14.6.2.2/5:
A class member access expression is type-dependent if the expression refers to a member of the current instantiation and the type of the referenced member is dependent, or the class member access expression refers to a member of an unknown specialization.
this->bar
does refer to a member of the current instantiation, but the type Bar
of the referenced member is not dependent. So now this->bar
is not type-dependent, and the name baz
in this->bar.baz
is looked up during the template definition as a non-dependent name. The template
keyword is not needed before baz
.