Effective C++ 笔记 —— Item 42: Understand the two meanings of typename.
Question: what is the difference between class and typename in the following template declarations?
template<class T> class Widget; // uses "class" template<typename T> class Widget; // uses "typename"
Answer: nothing. When declaring a template type parameter, class and typename mean exactly the same thing.
C++ doesn‘t always view class and typename as equivalent, however. Sometimes you must use typename.
Suppose we have a template for a function:
template<typename C> // print 2nd element in container; void print2nd(const C& container) { // this is not valid C++! if (container.size() >= 2) { C::const_iterator iter(container.begin()); // get iterator to 1st element ++iter; // move iter to 2nd element int value = *iter; // copy that element to an int std::cout << value; // print the int } }
To rectify the situation, we have to tell C++ that C::const_iterator is a type. We do that by putting typename immediately in front of it:
template<typename C> // this is valid C++ void print2nd(const C& container) { if (container.size() >= 2) { typename C::const_iterator iter(container.begin()); // ... } }
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. (Again, I'll describe an exception shortly.)
typename should be used to identify only nested dependent type names; other names shouldn't have it. For example, here's a function template that takes both a container and an iterator into that container:
template<typename C> // typename allowed (as is "class") void f(const C& container, // typename not allowed typename C::iterator iter); // typename required
The exception to the "typename must precede nested dependent type names" rule is that 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. For 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 mem. 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 //... } };
Let's look at one last typename example,
template<typename IterT> void workWithIterator(IterT iter) { typename std::iterator_traits<IterT>::value_type temp(*iter); // ... }
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.
Things to Remember:
- When declaring template parameters, class and typename are inter-changeable.
- Use typename to identify nested dependent type names, except in base class lists or as a base class identifier in a member initialization list.