error: use 'template' keyword to treat 'fun0' as a dependent template name
fun2(d).fun0<int>();
template
(As expected, if foo
is no longer a template struct, (1) compiles as well)
Why is bar::fun0
a dependent template name here? bar
doesn't depend on the template parameter T
of foo
.
Edit:
Clearly, bar::fun2
is responsible for the ambiguity that the .template
deal with. For instance, let's add the 2 following free functions:
fun3(const bar& d)
return d;
template <typename T>
fun4(const T& d)
return d;
fun3(d).fun0<int>()
and fun4(d).fun0<int>()
) also compile in the context of foo::fun1
. So the ambiguity is caused by the template parameter of foo
.
Why is fun2(d).fun0<int>()
not parsed as a call to a member function template?
–
–
–
I think this comes down to the rules in section 14.6.2
of the standard. Basically, I think the rules err on the side of caution in requiring you to use template
and typename
-- once you form an expression which might potentially be dependent, sometimes it will be declared so according to the rules, even though a human can plainly see that it isn't actually dependent. Then, there is a general rule 14.6.2.2.1
which states
Except as described below, an expression is type-dependent if any subexpression is type-dependent.
What I think is happening is that the expression fun2(d)
is declared to be type-dependent by the rules, even though that type is in fact the same in every instantiation of this (primary) template, as you and I can see -- this is why it's surprising that template
is required.
Under 14.6.2.1.4
in the C++11 (or C++14) standard,
A name is a dependent member of the current instantiation if it is a member of the current instantiation
that, when looked up, refers to at least one member of a class that is the current instantiation.
That means, the name fun2
is a dependent name, even though fun2
does not refer to T
or anything that explicitly depends on T
.
Thus when we consider the expression you gave fun2(d).fun0<int>()
, and consider the "member access subexpression", that is, fun2(d)
.
fun0<int>
-- intuitively we want to say that this is not dependent, as fun2(d)
is always of type bar
and fun0<int>
doesn't depend on T
either. There is rule 14.6.2.2.5
which states
A class member access expression (5.2.5) 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.
Neither of these conditions applies literally, since bar
is not the same type as the current instantiation (foo<T>
), and neither is fun0<int>
a member of an unknown specialization of the foo
template. This is why the expression d.fun0<int>()
is well-formed.
However, note clearly that this rule 14.6.2.2.5
comes after 14.6.2.2.1
which sets up the meaning of this entire section:
Except as described below, an expression is type-dependent if any subexpression is type-dependent.
Thus if any subexpression, such as fun2
, is formally "type-dependent", it poisons the entire expression, and causes similar rules to apply as if we were looking up members of template parameters or unknown specializations, etc. etc.
Specifically, the type-dependent
condition means that you need the template
prefix, because of rule 14.2.4:
When the name of a member template specialization appears after .
or ->
in a postfix-expression or after a
nested-name-specifier in a qualified-id, and the object expression of the postfix-expression is type-dependent
or the nested-name-specifier in the qualified-id refers to a dependent type, but the name is not a member of
the current instantiation (14.6.2.1), the member template name must be prefixed by the keyword template.
Otherwise the name is assumed to name a non-template.
Example:
struct X {
template<std::size_t> X* alloc();
template<std::size_t> static X* adjust();
template<class T> void f(T* p) {
T* p1 = p->alloc<200>(); // ill-formed: < means less than
T* p2 = p->template alloc<200>(); // OK: < starts template argument list
T::adjust<100>(); // ill-formed: < means less than
T::template adjust<100>(); // OK: < starts template argument list
— end example ]
Now very concretely:
By [14.2.4], in the postfix expression fun2(d)
.
fun0<int>
, if the object expression fun2(d)
is type-dependent, then you have to use template
prefix to call the member template.
Under [14.6.2.2.1], if fun2
is type-dependent then that forces fun2(d)
to be also.
And under [14.6.2.1.4], since fun2
when looked up refers to a member of the foo
class template, that alone is enough to make it a dependent member of the current instantiation.
I don't have a totally clear argument from any of the rules that if a name refers to a dependent member of the current instantiation, then that implies that the expression corresponding to the name is type-dependent
... and I have searched a few times now.
However, this viewpoint is advocated in @Johannes Schaub - litb's highly up-voted (but simplified, and non-normative) exposition of the rules:
Dependent names
The Standard is a bit unclear about what exactly is a dependent name. On a simple read (you know, the principle of least surprise), all it defines as a dependent name is the special case for function names below. But since clearly T::x also needs to be looked up in the instantiation context, it also needs to be a dependent name (fortunately, as of mid C++14 the committee has started to look into how to fix this confusing definition).
To avoid this problem, I have resorted to a simple interpretation of the Standard text. Of all the constructs that denote dependent types or expressions, a subset of them represent names. Those names are therefore "dependent names". A name can take different forms - the Standard says:
A name is a use of an identifier (2.11), operator-function-id (13.5), conversion-function-id (12.3.2), or template-id (14.2) that denotes an entity or label (6.6.4, 6.1)
An identifier is just a plain sequence of characters / digits, while the next two are the operator + and operator type form. The last form is template-name . All these are names, and by conventional use in the Standard, a name can also include qualifiers that say what namespace or class a name should be looked up in.
Would be great to get a more concrete explanation of that last part.
–
–
–
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.