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. 

 

posted @ 2021-12-10 11:17  MyCPlusPlus  阅读(35)  评论(0编辑  收藏  举报