条款42:了解 typename 的双重含义(Understand the two meanings of typename)
条款42:了解 typename 的双重含义
1.1 声明模板参数
下面的两个模板声明完全一样!!!
template<class T> class Widget; // 使用关键词 "class"
template<typename T> class Widget; // 使用关键词 "typename"
1.2 嵌套的从属名称
- 然而,class和typename并不总是等价的。有时必须使用typename。
template<typename C>
void print2nd(const C& container) // 打印容器中的第二个元素;
{ // 这不是有效的C++代码!
if (container.size() >= 2) {
//C::const_iterator是一个嵌套的从属名称。int是一个非从属名称。
C::const_iterator iter(container.begin()); // 取得第一个元素的迭代器
++iter; // 将iter移动到第二元素
int value = *iter; // 将该元素拷贝到一个int变量
std::cout << value; // 打印该int值
}
}
- 嵌套的从属名称可能会导致解析困难。
template<typename C>
void print2nd(const C& container)
{
C::const_iterator* x;
...
}
- 默认情况下,嵌套的从属名称不是类型。
我们必须告诉C++编译器, C::const_iterator是一种类型:
template<typename C> // 这是有效的C++代码
void print2nd(const C& container)
{
if (container.size() >= 2) {
typename C::const_iterator iter(container.begin());
...
}
}
- typename只能用于标识嵌套的从属类型名称。
template<typename C> // 允许使用typename(就像"class"一样)
void f(const C& container, // 不可以使用typename
typename C::iterator iter); // 必须使用typename
1.3 一个例外
一个例外:typename不能放在基类列表中的嵌套从属类型名称之前,也不能放在成员初始化列表中的基类标识符之前。
template<typename T>
class Derived : public Base<T>::Nested { // 基类列表: 不能使用typename
public:
explicit Derived(int x)
: Base<T>::Nested(x) // 初始化列表中:不能使用typename
{
typename Base<T>::Nested temp; // 其他情况下,需要使用typename
... //指明是嵌套从属类型名称
}
...
};
假设我们正在编写一个接受迭代器的函数模板,并希望创建迭代器指向的对象的本地副本temp。
template<typename IterT>
void workWithIterator(IterT iter)
{
typedef typename std::iterator_traits<IterT>::value_type value_type;
value_type temp(*iter);
...
}
1.4 总结
- 声明模板参数时,class和typename可以互换。
- 使用typename关键字标识嵌套从属类型名称;但也有例外:基类列表或作为一个基类成员初始化列表中除外。