struct Bar {
template<typename>
void baz() {
}
};
template<typename>
struct Foo {
Bar bar;
Foo() {
bar.baz<int>();
}
};
int main() {
return 0;
}
This code compiles fine (in GCC 4.7), but if I prefix the call to bar.baz<int>()
with this->
, baz
becomes a dependent name that needs disambiguating with template
.
bar.baz<int>(); // OK
this->bar.baz<int>(); // error
this->bar.template baz<int>(); // OK
Surely this->bar
can only refer to Bar bar
, whose member baz
is clearly a template? Why does the addition of this->
make this code ambiguous to the compiler?
p.s. Originally, bar
was a data member of a base class template which needed disambiguating with this->
, but I have simplified the example for the purpose of this question.
Function templates. Function templates are special functions that can operate with generic types. This allows us to create a function template whose functionality can be adapted to more than one type or class without repeating the entire code for each type. In C++ this can be achieved using template parameters.
A dependent name is a name that depends on the type or the value of a template parameter. For example: template<class T> class U : A<T> { typename T::B x; void f(A<T>& y) { *y++; } }; The dependent names in this example are the base class A<T> , the type name T::B , and the variable y .
Member functions can be function templates in several contexts. All functions of class templates are generic but are not referred to as member templates or member function templates. If these member functions take their own template arguments, they are considered to be member function templates.
A dependent name is essentially a name that depends on a template parameter. A dependent name can be a type, a non-type, or a template parameter. To express that a dependent name stands for a type or a template, you have to use the keywords typename or template .
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
a template parameter,
...
a [simple-]template-id in which either the template name is a template parameter or any of the template arguments is a dependent type or an expression that is type-dependent or value-dependent,
[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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With