侧边栏壁纸
博主头像
如此肤浅

但行好事,莫问前程!

  • 累计撰写 52 篇文章
  • 累计创建 6 个标签
  • 累计收到 6 条评论

目 录CONTENT

文章目录

条款42 了解 typename 的双重意义

如此肤浅
2022-06-10 / 0 评论 / 0 点赞 / 26 阅读 / 2,521 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-06-10,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1. 声明 template 参数时,前缀关键字 class 和 typename 可互换。
2. 请使用关键字 typename 标识嵌套从属类型名称;但不得在 base class lists(基类列)或 member initialization list(成员初值列)内以它作为 base class 修饰符。

在 template 声明式中,class 和 typename 有什么不同?

template<class T> class Widget;
template<typename T> class Widget;

当声明 template 类型参数时,class 和 typename 意义完全相同。但有时候只能使用 typename。

从属名称与非从属名称

现有一个 template function,接受一个 STL 容器为参数,容器内存放 ints。现假设该函数的功能仅仅为打印其第二个元素的值:

template<typename C>
void print2nd(const C& container) // 打印容器内第二个元素
{
	if(container.size() >= 2) {
    	C::const_iterator iter(container.begin());
        ++iter;
        int value = *iter;
        std::cout << value;
    }
}

局部变量 iter 的类型是 C::const_iterator,实际是什么取决于 template 参数 C。template 内出现的名称如果相依存于某个 template 参数,我们称它为从属名称(dependent names)。如果从属名称在 class 内呈嵌套状,我们称它为嵌套从属名称(nested dependent name),C::const_iterator 就是这样一个名称。实际上它还是个嵌套从属类型名称(nested dependent type name),也就是个嵌套从属名称并且指涉某类型。

另一个局部变量 value 其类型是 int,int 是一个并不依赖任何 template 参数的名称,这样的名称是非从属名称(non-dependent names)。

嵌套从属名称引起解析困难

嵌套从属名称可能导致解析困难,例如:

template<typename C>
void print2nd(const C& container)
{
	C::const_iterator* x;
    ...
}

看起来好像我们声明 x 为一个局部变量,它是个指针,指向一个 C::const_iterator。但它之所以被那么认为,只因为我们“已经知道” C::const_iterator 是个类型。如果C::const_iterator 不是个类型呢?如果 C 有个 static 成员变量而碰巧被命名为 const_iterator,或如果 x 碰巧是个 global 变量名称呢?那样的话上述代码就不再是声明一个局部变量,而是一个相乘的动作:C::const_iterator 乘以 x。

在我们知道 C 是什么之前,没有任何办法可以知道 C::const_iterator 是否为一个类型。而当编译器开始解析 template print2nd 时,尚未确知 C 是什么东西。cpp 有个规则可以解析这一歧义状态:如果解析器在 template 中遭遇一个嵌套从属名称,它便假设这名称不是个类型,除非你告诉它是。所以缺省情况下嵌套从属名称不是类型。 此规则有个例外,稍后会提到。

此时我们再来看下面这段代码:

template<typename C>
void print2nd(const C& container)
{
	if(container.size() >= 2) {
    	// 这个名称被假设为非类型
    	C::const_iterator iter(container.begin()); 
        ...
    }
}

经过上述分析我们知道上述代码不是有效的 C++ 代码。iter 声明式只有在 C::const_iterator 是个类型时才合理,但我们并没有告诉 C++ 说它是,于是 C++ 假设它不是。若我们想要让 C++ 知道 C::const_iterator 是个类型,我们只需在它前面放个关键字 typename 即可:

// 此时才是合法的C++代码
template<typename C>
void print2nd(const C& container)
{
	if(container.size() >= 2) {
    	typename C::const_iterator iter(container.begin());
        ...
    }
}

一般性规则: 任何时候当你想要在 template 中设置一个嵌套从属类型名称,就必须在紧临它的前一个位置放上关键字 typename。(但后面会谈到一个例外)

typename 只被用在嵌套从属类型名称上,不该使用在其他名称上,例如下面这个模板函数,接受一个容器和一个“指向该容器”的迭代器:

template<typename C> // 允许使用typename(或class)
void f(const C& container, // 不允许使用typename
	typename C::iterator iter); // 一定要使用typename

typename 必须作为嵌套从属类型名称的前缀词的例外情况

typename 不可以出现在 base classes list 内的嵌套从属类型名称之前,也不可在 member initialization list(成员初值列)中作为 base class 修饰符。例如:

template<typename T>
// base class list中不允许typename
class Derived: public Base<T>::Nested {
public:
	// mem init list中不允许typename嵌套从属类型名称
	explicit Derived(int x): Base<T>::Nested(x)
    {
    	// 即不在base class list中也不在mem init list中,
        // 
    	typename Base<T>::Nested temp;
        ...
    }
    ...
};
0

评论区