typename的一些用法和注意问题

Posted litifeng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了typename的一些用法和注意问题相关的知识,希望对你有一定的参考价值。

 

 

一些关键概念
在我们揭开真实原因的面纱之前,先保持一点神秘感,因为为了更好的理解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>
struct MyClass {
    int i;
    vector<int> vi;
    vector<int>::iterator vitr;
    T t;
    vector<T> vt;
    vector<T>::iterator viter;

};

因为是内置类型,所以类中前三个定义的类型在声明这个模板类时就已知。然而对于接下来的三行定义,只有在模板实例化时才能知道它们的类型,因为它们都依赖于模板参数T。因此,T, vector<T>和vector<T>::iterator称为依赖名。前三个定义叫做非依赖名。
更为复杂一点,如果用了typedef T U; U u;,虽然T没再出现,但是U仍然是依赖名。由此可见,不管是直接还是间接,只要依赖于模板参数,该名称就是依赖名。

typename的标记作用:
结束以上两个个概念的讨论,让我们接着揭开typename的神秘面纱。
一个例子
在Stroustrup起草了最初的模板规范之后,人们更加无忧无虑的使用了class很长一段时间。可是,随着标准化C++工作的到来,人们发现了模板这样一种定义:

template <typename T>
void foo() {
    T::iterator iter;
    // ...
}

在上例代码中T本身已经是模板的类型参数,它只有等到模板实例化时才会知道是哪种类型,更不用说由T定义的内部的iterator

编译器不知道第三行代码到底是,定义一个变量还是定义一个新类型,这样同一行代码能以两种完全不同的方式解释,而且在模板实例化之前,完全没有办法来区分它们,这绝对是滋生各种bug的温床。这时C++标准委员会再也忍不住了,与其到实例化时才能知道到底选择哪种方式来解释以上代码,委员会决定引入一个新的关键字,这就是typename

 

c++标准定义到:

对于用于模板定义的依赖于模板参数的名称,只有在实例化的参数中存在这个类型名,或者这个名称前使用了typename关键字来修饰,编译器才会将该名称当成是类型。除了以上这两种情况,绝不会被当成是类型。

因此,如果你想直接告诉编译器T::iterator是类型而不是变量,只需用typename修饰:

template <typename T>
void foo() {
    typename T::iterator iter;
    // ...
}

通过加上typename关键字,这样编译器就可以确定T::iterator是一个类型,而不再需要等到实例化时期才能确定,因此消除了歧义。

以上是关于typename的一些用法和注意问题的主要内容,如果未能解决你的问题,请参考以下文章

为啥 GraphQL 片段在查询中需要 __typename?

c++中typenametypedef以及using关键字用法

typename的用法

C++模板typename关键字的用法

C++模板typename关键字的用法

c++中typenametypedef以及using关键字用法