Difference between typename
and class
in template declarations
template<class T> class Widget; // uses “class”
template<typename T> class Widget; // uses “typename”
No difference. When declaring a template type parameter, class and typename mean exactly the same thing. Some programmers prefer class all the time, because it’s easier to type. Others (including me) prefer typename, because it suggests that the parameter need not be a class type.
However, class
and typename
are not always the same
Terminology
- Dependent Names : Names in a template that are dependent on a template parameter
- Nested Dependent Name : a dependent name is nested inside a class.
- Nested Dependent Type Name: a nested dependent name that refers to a type. To identify a Nested Dependent Type Name, we can check if the type name is nested inside a class that depends to a template parameter.
Example:std::iterator_traits<IterT>::value_type temp(*iter);
value_type
is a Nested Dependent Type Name nested insideiterator_traits<IterT>
which depends onIterT
- Non-dependent Names: name that does not depend on any template parameter
Problem
In the example:
template<typename C> // print 2nd element in container;
void print2nd(const C& container) { // this is not valid C++!
C::const_iterator * x;
...
}
C::const_iterator
is a Nested Dependent Name as it nested in C
and it looks like a type.
But it looks that way only because we “know” that C::const_iterator
is a type. But what if C::const_iterator
weren’t a type? What if C had a static data member that happened to be named const_iterator
, and what if x
happened to be the name of a global variable? In that case, the code above wouldn’t declare a local variable, it would be a multiplication of C::const_iterator
by x
!
Solution
C++ has a rule to resolve this ambiguity: if the parser encounters a nested dependent name in a template, it assumes that the name is not a type unless you tell it otherwise. By default, nested dependent names are not types.
Then how to tell C++ that a nested dependent name is a type?
We do that by putting typename immediately in front of it.
template<typename C> // print 2nd element in container;
void print2nd(const C& container) {
typename C::const_iterator * x;
...
}
The general rule is simple: anytime you refer to a nested dependent type name in a template, you must immediately precede it by the word typename
typename
should be used to identify only nested dependent type names; other names shouldn’t have it
template<typename C> // typename allowed (as is “class”)
void f(
const C& container, // typename not allowed
typename C::iterator iter // typename required
);
An Exception of the Above Rule
typename
must NOT precede nested dependent type names in a list of base classes or as a base class identifier in a member initialization list
Example:
template<typename T>
class Derived: public Base<T>::Nested{//base class list:typename not allowed
public:
explicit Derived(int x)
: Base<T>::Nested(x) // base class identifier in memory
{ // init. list: typename not allowed
typename Base<T>::Nested temp; // use of nested dependent type
... // name not in a base class list or
} // as a base class identifier in a
... // mem. init. list:
// typename required
};
Avoid writing tedious type declaration multiple time
template<typename IterT>
void workWithIterator(IterT iter) {
typename std::iterator_traits<IterT>::value_type temp(*iter);
...
}
Don’t let the std::iterator_traits::value_type startle you. That’s just a use of a standard traits class (see Item 47), the C++ way of saying “the type of thing pointed to by objects of type IterT.”
The statement declares a local variable (temp) of the same type as what IterT objects point to, and it initializes temp with the object that iter points to.
Because std::iterator_traits::value_type is a nested dependent type name (value_type is nested inside iterator_traits, and IterT is a template parameter), we must precede it by typename.
Typing typename std::iterator_traits<IterT>::value_type
more than once is ghastly, so you’ll want to create a typedef
template<typename IterT>
void workWithIterator(IterT iter) {
typedef typename std::iterator_traits<IterT>::value_type value_type;
value_type temp(*iter);
...
}
Portability
I should mention that enforcement of the rules surrounding typename
varies from compiler to compiler. This means that the interaction of typename
and nested dependent type names can lead to some mild portability headaches.
Things to Remember
- When declaring template parameters, class and typename are interchangeable.
- Use typename to identify nested dependent type names, except in base class lists or as a base class identifier in a member initialization list.