Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template dependent typename

Consider the following code:

struct bar
{
  template <typename U>
  void fun0() const {}
};

template <typename T>
struct foo
{
  void
  fun1(const bar& d)
  {
    // (1) KO
    fun2(d).fun0<int>();
    // (2) OK
    fun2(d).template fun0<int>();
    // (3) OK        
    d.fun0<int>();
  }

  bar
  fun2(const bar& d)
  {
    return d;
  }
};

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:

bar
fun3(const bar& d)
{
  return d;
}

template <typename T>
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?

like image 831
Alexandre Hamez Avatar asked Jan 09 '16 17:01

Alexandre Hamez


People also ask

Which is Dependant on template parameter?

Which is dependant on template parameter? Explanation: Base class is dependant on template parameter.

What is typename in template?

" typename " is a keyword in the C++ programming language used when writing templates. It is used for specifying that a dependent name in a template definition or declaration is a type.

What is dependent name example?

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 .

What is the difference between template typename T and template T?

There is no difference. typename and class are interchangeable in the declaration of a type template parameter.


1 Answers

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:

  1. 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.

  2. Under [14.6.2.2.1], if fun2 is type-dependent then that forces fun2(d) to be also.

  3. 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.

like image 87
Chris Beck Avatar answered Sep 23 '22 12:09

Chris Beck