Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Dependent Names and Nondependent Names, Part II

Last updated Jan 1, 2003.

To ensure that template definitions are parsed correctly, certain dependent names require disambiguation. Find out when disambiguation is required and how it is done.

Disambiguation of Dependent Names

A qualified dependent name such as T::x can be one of the following:

  • A nontype (i.e., an object or a function)
  • A type
  • A template

By default, the compiler assumes that the qualified dependent name is a nontype:

template <class T> struct A{
enum {x}; //x is a constant, not a type
}; 
template <class T> struct B{
 void func() {A<T>::x*y; } //multiplication, not declaration
};

To override the default, the programmer must indicate explicitly that the qualified dependent name is a type or a template.

Indicating a Template Parameter with typename

The keyword typename has two uses: disambiguation (which I will show shortly), and the more trivial role of inidicating a template parameter:

template <class T> struct S {};
template <typename T> struct S {};//identical to S above

In other words, typename in this context is synonymous with class. Personally, I prefer to use the old-fashioned class to declare a template parameter because it’s shorter and because I prefer to see typename doing exactly what it’s was meant to do when it was added to C++ -- disambiguate qualified dependent names.

Disambiguation with typename

The typename keyword must prefix a dependent name when that name satisfies the following three rules:

  • It appears inside a template
  • It’s qualified
  • It isn’t used in a base class specification or a member initialization list.

The following example uses typename in several places, some of which are mandatory, whereas others are erroneous:

template <class T> class D: typename C<T>::Base { //#1 error
 D(): typename C<T>::Base(typename C<T>::Base(0)) {}//#2 error, #3 correct
 typename C<T> f() { //#4 error
 typename C<T>::A* p; //#5 correct
 A<T>::B * n;
 }
};
class Q{
 typename C<long>::A * p; // #6 error
}
template <class T, class U> class R{
 typename C<long>::A * p; // #7 optional
}

There are six occurrences of typename in the example, most of which are erroneous. Let’s look at each one of them carefully.

typename # 1: error. This is a violation of the third rule which says that base classes should not be prefixed by typename. Remember that a list of base classes can only include class types anyway, so disambiguation isn’t needed here.

typename #2: error. The use of typename to indicate a member initializer violates the third rule.

typename #3: correct. Here, typename is used for disambiguating the type of a parameter. Without typename, the expression C<T>::Base(0) would be treated as a call to a function called Base. With the typename prefix, C<T>::Base(0) creates a temporary object of type C<T>::Base initialized with the argument 0.

typename #4: error. This is a violation of the second rule, which requires that the name be qualified.

typename #5: correct. This is a pointer declaration, not multiplication.

typename #6: error. Here typename is used not inside a template and therefore it’s a violation of the first rule.

typename #7: Optional. Although it satisfies all the three rules above, the name C<long>::A isn’t dependent on any of R’s template parameters.

Disambiguation using template

A dependent qualified name may denote a template, or more specifically, a member template. These cases are rare, but they warrant your attention because of the unusual syntax that’s needed for disambiguation.

The following example shows when template is needed for disambiguation:

template <class T, class Alloc=std::allocator<T>>

class List
{
 struct Node {
 //...
};
 typedef A::rebind<Node>::other Node_allocator; //compilation error
};

There two problems for the compiler here. The qualified name rebind is assumed to be a nontype rather than a template. Since rebind is considered as an object, the < symbol is treated as the less-than sign, not as a template parameter indicator. This isn’t all, though. Even if the compiler were informed that rebind is a template name, another parsing error would occur when the name other is parsed because the compiler assumes here too that other is a nontype. To disambiguate this expression, both typename and template are needed:

 typedef typename A::template rebind<Node>::other Node_allocator; //OK

This declaration is parsed correctly now. The prefix template instructs the compiler that rebind is a template. Consequently, the compiler now understands that the angle brackets denote a template parameter. The use of typename instructs the compiler that the name other is a type.

The template keyword may also be used after the -> and . operators. Generally speaking, template is used when a dependent name appears before a qualifying operator (::, ->, .), and a template-id appears after the qualifying operator. Recall that a template-id is a template name followed by template arguments in angle brackets. Look at another example:

m. template D<T>::func();

Even without looking at the definition of D, you can tell that D<T> is a member template of m, that m is a dependent name, and that func is a nontype declared inside the template D.

In conclusion

Compilers are required to fully parse template definitions immediately. To enable that, the compiler must know whether a name used in a template is dependent on the template parameter(s) or not. If the name doesn’t depend on a template parameter, it is said to be nondependent and it is resolved at the point of definition. If the name is dependent and qualified, the compiler assumes that that name denotes a nontype. To override this default behavior, the programmer must use typename and template to indicate a type or a template, respectively.