typename的双重意义

《Effective C++》 Note

下面有两种template声明:

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

当我们声明template类型参数,class和typename没有什么不同。但是使用typename可以暗示参数并非一定得是个class类型

C++并不总是把class和typename视为等价,有时候你一定得使用typename。

如下这个例子:

#include <iostream>
#include <vector>

template<typename C>
void print2nd(const C& container)
{
    if (container.size() >= 2)
    {
        C::const_iterator iter(container.begin());  //取得第一元素的迭代器
        ++iter;                //迭代器移向第二元素
        int value = *iter;     //将第二元素复制到value
        std::cout << value;    //输出value
    }
}

int main()
{
    std::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    print2nd(v);

    return 0;
}

这个模板函数,接受一个STL兼容容器为参数,容器内持有的对象可被赋值为int。这个函数仅仅打印传进来容器的第二元素值。在VS 2015上可成功编译并运行,别的环境不知。

iter的类型是C::const iterator,到底是什么取决于template参数C。template内出现的名称若相依于某个template参数,称为从属名称(dependent names),若从属名称在class内呈嵌套状,称其为嵌套从属名称(nested dependent name)。C::const_iterator便是个嵌套从属类型名称。

value是个local变量,其类型为int,int并不倚赖任何template参数的名称,这样的名称称为非从属名称(non-dependent names)

嵌套从属类型有可能导致解析(parsing)困难。假设令print2nd这样:

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

看起来声明的x为一个local变量,是个指针,指向一个C::const_iterator。之所以这么认为,只是我们已经知道C::const_iterator是个类型。若C::const_iterator不是个类型呢?如果C有个static成员变量而碰巧被命名为const_iterator,或x正好是个global变量名称呢?

那么上面的代码就不再是声明一个local变量,而是一个相乘操作:C::const_iterator * x。这是完全有可能的!

在知道C是什么之前,没有任何办法可以知道C::const_iterator是否为一个类型。C++有个规则可以解析这一歧义状态:如果解析器在template中遭遇一个嵌套从属名称,它便假设这名称不是个类型,除非你告诉它。所以缺省情况下嵌套从属名称不是类型。

所以上面的例子不是有效的C++代码,虽然它在这个机器上编译运行都无问题。iter声明式只有在C::const_iterator是个类型时才合理,但C++并不知道它是个合理的类型。我们需要告诉它,只需在紧临它之前放置关键字typename:

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不可以出现在base classes list内的嵌套从属名称之前,也不可在member initialization list中作为base class修饰符。如:

template<typename T>
class Derived: public Base<T>::Nested{   //base class list中不允许使用"typename"
public:
    explicit Derived(int x) : Base<T>::Nested(x)  //mem.init.list中不允许使用"typename"
    {
        //嵌套从属类型,既不在base class list中也不在mem.init.list中
        //作为一个base class修饰符需加上typename
        typename Base<T>::Nested temp;
    }
}

假设我们在写一个function template,它接受一个迭代器,而我们打算为该迭代器的对象做一份local复件temp。可以这样写:

template<typename, IterT>
void workWithIterator(IterT iter)
{
    typename std::iterator_traits<IterT>::value_type temp(*iter);
    ....
}
由于std::iterator_traits<IterT>::value_type是个嵌套从属类型名称,
value_type被嵌套于iterator_traits<IterT>之内而IterT是个template参数,所以我们必须将其前放置typename。

原文地址:https://www.cnblogs.com/coolcpp/p/typename.html

时间: 2024-08-25 15:38:01

typename的双重意义的相关文章

Effective C++ Item 42 了解 typename 的双重意义

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:声明 template 参数时,前缀关键字 class 和 typename 可互换.请使用关键字 typename 标识嵌套从属类型名称: 示例1: template<typename C> void print2nd(const C &container){ C::const_iterator *x;//歧义.如果const_iterator是个static成员变量,x

Effective C++:条款42:了解typename的双重意义

(一) template声明式中,class和typename这两个关键字意义完全相同 template<class T> class Widget; template<typename T> class Widget; (二) template <typename C> void print2nd(const C& container) { if (container.size() >= 2) { C::const_iterator iter(conta

条款42:了解typename的双重含义

typename在很多种情况下与class是完全相同的,例如下面的使用: 1 templame<typename T> 2 ...... 3 template<class T> 4 ...... 1 template<typename C> 2 void print2nd(const C containter) 3 { 4 if(containter.size() >= 2){ 5 C::const_iterator iter(containter.begin()

《Effective C++》读书笔记汇总

我之前边读<Effective C++>边写下每个条款的读书笔记,这一版是C++11之前的版本.这里我将每个条款令我印象深刻的点小结一下. 1.C++包括:Plain C(面向过程).OOP(面向对象).模板(泛型和模板元编程).STL(C++标准库). 2.用inline.enum.const代替#define.#define定义的宏,一旦复杂起来,高手都很难掌控.不要带入C的习惯. 3.灵活使用const前缀.不需要进行改变的数据加上const前缀.指针的const前缀有两种形式,cons

《Effective C++》:条款41-条款42

条款41了解隐式接口和编译期多态 条款42了解typename的双重意义 条款41:了解隐式接口和编译期多态 面向对象编程总是以显示接口(explicit interfaces)和运行期多态(runtime polymorphism)来解决问题.例如 class Widget{ public: Widget(); virtual ~Widget(); virtual std::size_t size() const; virtual void normalize(); void swap(Wid

《effective c++》读书笔记4

条款41:了解隐式接口和编译期多态 n  classes和templates都支持接口和多态 n  对classes而言接口是显式的,以函数签名为中心,多态则是通过虚函数发生于运行期. n  对templates参数而言,接口是隐式的,奠基于有效表达式,多态则是通过template具现化和函数重载解析发生于编译器. 条款42:了解typename的双重意义 n  声明模板参数,前缀关键字class和typename可互换. n  请使用关键字typename标识嵌套从属类型名称,但不得在基类或者

C++基础和STL,Effective C++笔记

C++基础 static static变量存储在静态数据区 相对于function:在函数内,变量,内存只被分配一次,多次调用值相同 相对于其他模块(.c文件):变量和函数,不能被模块外其他函数访问(private) 相对于类:类中的static变量和函数属于整个类,而不是对象 全局变量 VS 全局静态变量 若程序由一个源文件构成时,全局变量与全局静态变量没有区别. 若程序由多个源文件构成时,全局变量与全局静态变量不同:全局静态变量使得该变量成为定义该变量的源文件所独享,即:全局静态变量对组成该

《Effective C++》 目录

目录 条款1:视C++为一个语言联邦 条款2:尽量以const.enum.inline替换#define 条款3:尽可能使用const 条款4:确定对象使用前已先被初始化 条款5:了解C++默认编写并调用哪些函数 条款6:若不想使用编译器自动生成的函数,就该明确拒绝 条款7:为多态基类声明virtual析构函数 条款8:别让异常逃离析构函数 条款9:绝不在构造和析构过程中调用virtual函数 条款10:令operator=返回一个reference to *this 条款11:在operato

Effective C++ Notes(读书笔记)

1,视C++为一种语言联邦,大致分为4个部分: A)C.说到底C++仍是以C为基础.区块.语句.预处理器.内置数据类型.数组.指针等等统统来自C. B)Object-Oriented C++.这部分也就是C with Classes所诉求的:classes(包括构造函数和虚构函数).封装.继承.多态,虚函数等等. C)Template C++.这是C++的范型编程部分,tamplates威力强大,它给我们带来了崭新的编程范型,也就是所谓的TMP模板元编程. D)STL.STL是个template