typedef 以及 typename 的作用

在学习STL的过程当中,经常会遇到一些让人难以理解的C++代码,比如:

typedef typename std::vector<T>::size_type size_type;

看起来它应该是定义一个类型别名,但是typedef使用方式应该是typedef + 原类型名 + 新类型名(typedef具体使用方法可移步 typedef和#define的用法与区别 ),为何此处多了个typename?typename又是什么东西?

typedef char* PCHAR;

只记得泛型编程(GP)当中,若是对模板类/函数进行定义时,可以使用template或者template,且这两者是等价的。
那么如上语句中的typename关键字是起了什么作用呢?

1. std::vector::size_type 是什么?

查看侯捷的《STL源码剖析》一书,其为:

template <class T,class Alloc=alloc>
class vector{
public:
    //...
    typedef size_t size_type;
    //...
};

原来vector::size_type是vector中的嵌套类型,其实际等价于STL中大面积使用的可跨平台的size_t类型。

  1. 使用 typename 关键字的意义
    将语句中的typename关键字抛开不看,则语句为:

typedef std::vector::size_type size_type;
1
可以感觉到是对std::vector::size_type这个类型进行重命名。那么为什么要加上typename这个关键字呢?

原来,其中T是模板中的类型参数,它只有等到模板实例化时才会知道是哪种类型,更不用说T内部的size_type。所以对于限定依赖名(解释见补充)std::vector::size_type而言,无法判断其是静态成员变量/函数还是嵌套类型,这样会造成语句的二义性,这是不能够容忍的。

若是在std::vector::size_type这个类型前加上typename这一关键字,指明这是一个嵌套类型,而不是T的静态成员或静态成员函数,消除了二义性。

  1. 总结
    根据以上的分析,可以知道语句的含义和作用是:
    typedef创建了存在类型的别名,而typename告诉编译器std::vector::size_type是一个类型而不是一个成员。

拓展:为何会有typename关键字
若是不加 typename 时,在如下模板定义会造成语句的二义性:


template <class T>
void foo() {
    T::iterator * iter;
    // ...

}

struct ContainsAnotherType {
    static int iterator;
    // ...

};

然后如此实例化foo的类型参数:

foo<ContainsAnotherType>();

那么,T::iterator * iter;被编译器实例化为ContainsAnotherType::iterator * iter;。在这里,前面是一静态成员变量而不是类型,那么这便成了一个乘法表达式,不过iter在这里没有定义,编译器会报错:

error C2065: ‘iter’ : undeclared identifier

但如果iter是一个全局变量,那么这行代码将完全正确,它是表示计算两数相乘的表达式,返回值被抛弃。

如上造成了同一行代码能以两种完全不同的方式解释,而且在模板实例化之前,完全没有办法来区分它们。

为解决该问题,C++标准委员会引入了typename关键字,使得模板类/函数在实例化前就区分其内部的依赖名(例如:vector::iterator viter;, 也称dependent names)为静态成员变量/函数还是嵌套类型。

补充:类外部访问类内成员或名称、(非)限定名、(非)依赖名
一、 类外部访问类内成员或名称

在类作用域概念中,在类外部访问类中的名称时,可以使用类作用域操作符::,形如MyClass::name的调用通常存在三种:①静态数据成员、②静态成员函数和③嵌套类型:

struct MyClass {
    static int A;
    static int B();
    typedef int C;
}

MyClass::A, MyClass::B, MyClass::C分别对应着上面三种。

二、限定名和非限定名

限定名(qualified name),就是限定了命名空间的名称。看下面这段代码,cout和endl就是限定名:

#include <iostream>
int main()  {
    std::cout << "Hello world!" << std::endl;
}

cout和endl前面都有std::,它限定了std这个命名空间,因此称其为限定名。

如果在上面这段代码中,前面用using std::cout;或者using namespace std;,然后使用时只用cout和endl,它们的前面不再有空间限定std::,所以此时的cout和endl就叫做非限定名(unqualified name)。

三、依赖名和非依赖名

依赖名(dependent name) 是指依赖于模板参数的名称,而 非依赖名(non-dependent name) 则相反,指不依赖于模板参数的名称。看下面这段代码:

template <class T>
class MyClass {
    int i;
    vector<int> vi;
    vector<int>::iterator vitr;

    T t;
    vector<T> vt;
    vector<T>::iterator viter;
};

因为是内置类型,所以类中前三个定义的类型在声明这个模板类时就已知。然而对于接下来的三行定义,只有在模板实例化时才能知道它们的类型,因为它们都依赖于模板参数T。因此,T, vector和vector::iterator称为依赖名。前三个定义叫做非依赖名。

更为复杂一点,如果用了typedef T U; U u;,虽然T没再出现,但是U仍然是依赖名。由此可见,不管是直接还是间接,只要依赖于模板参数,该名称就是依赖名。

参考博客:
typedef typename 的作用

已标记关键词 清除标记
相关推荐
<p> <strong><span style="font-size:20px;color:#FF0000;">本课程主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者</span></strong> </p> <p> <span style="color:#FF0000;"><strong><span style="font-size:18px;">1. 包含:<span style="color:#FFFF00;background-color:#FF0000;">项目源码、</span><span style="color:#FFFF00;background-color:#FF0000;">项目文档、数据库脚本、软件工具</span>等所有资料</span></strong></span> </p> <p> <span style="color:#FF0000;"><strong><span style="font-size:18px;">2. 手把手的带你从零开始部署运行本套系统</span></strong></span> </p> <p> <span style="color:#FF0000;"><strong><span style="font-size:18px;">3. 该项目附带的源码资料可作为毕设使用</span></strong></span> </p> <p> <span style="color:#FF0000;"><strong><span style="font-size:18px;">4. 提供技术答疑和远程协助指导</span></strong></span><strong><span style="font-size:18px;"></span></strong> </p> <p> <br /> </p> <p> <span style="font-size:18px;"><strong>项目运行截图:</strong></span> </p> <p> <strong><span style="font-size:18px;">1)系统登陆界面</span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241015433522.png" alt="" /><br /> </span></strong> </p> <p> <strong><span style="font-size:18px;"><strong><span style="font-size:18px;">2)学生模块</span></strong></span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241015575966.png" alt="" /></span></strong> </p> <p> <strong><span style="font-size:18px;"><strong><span style="font-size:18px;">3)教师模块</span></strong></span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241016127898.png" alt="" /></span></strong> </p> <p> <strong><span style="font-size:18px;"><strong><span style="font-size:18px;">4)系统管理员</span></strong></span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241016281177.png" alt="" /></span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241016369884.png" alt="" /></span></strong> </p> <p> <strong><span style="font-size:18px;"><br /> </span></strong> </p> <p> <strong><span style="font-size:18px;"><strong><span style="font-size:18px;">更多Java毕设项目请关注我的毕设系列课程 <a href="https://edu.csdn.net/lecturer/2104">https://edu.csdn.net/lecturer/2104</a></span></strong></span></strong> </p> <p> <strong><span style="font-size:18px;"><br /> </span></strong> </p>
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页