Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

Lines (2) and (3) compile, but (1) fails with:

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?

@AlanStokes How exactly would you specialise fun2 in a way that does allows the return type to be changed, yet still keeps the fun1 around? – user743382 Jan 9, 2016 at 17:15 @AlanStokes You can only specialise fun2 to return a covariant type (so it must be derived from bar), so the covariant type is guaranteed to have the same functionality (i.e. it must have a template <typename> void fun0() const function). So it will always return a type that is actually a bar – texasflood Jan 9, 2016 at 17:21 @AlanStokes You can't return a baz. fun2 is already declared as returning bar. You need to specialise the whole template, not just the member, to allow the return type to be different, and when specialising the whole template, fun1 won't be kept around. – user743382 Jan 9, 2016 at 17:47

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.

    Thank you for this nice answer. So it seems it all boils down to a "too broad" definition of dependent names in the C++ standard? – Alexandre Hamez Jan 12, 2016 at 7:15 Sure, that's one way to look at it. Whether it's a "defect" is a separate issue -- the fact is that the C++ grammar is ambiguous, and writing a performant and correct parser is difficult. Requiring template in cases like this may allow a parser to be written which is faster and uses less memory. Later the compiler knows the type of fun2(d), but at the parser stage, it's just trying to figure out if in fun0<int> the < is a comparison operator or the start of a template parameter list -- at the time that template and typename are relevant it knows barely anything about your program. – Chris Beck Jan 12, 2016 at 8:41 Lowering strain on the parser is sure nice but adding pressure on the programmer who have to think and figure out why them has to put template in such cases could turn out to be net negative after all. Well maybe if only all the compilers could give nice error messages like clang in such case, it wouldn't matter that much. – Predelnik Dec 19, 2018 at 8:50

    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.