Effective C++: Item 42 -- Understand the two meanings of typename

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 inside iterator_traits<IterT> which depends on IterT
  • 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.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值