C++模板(template)中typename

1、typename关键字

  在声明template参数时, 前缀关键字class和typename可以互换,但在使用模板参数T的内部类型名称即嵌套从属名称时只能用typename。

  在C++标准化的过程中,引入关键字typename是为了说明:模板类型参数内部的标识符(associated type,常见于STL中的各种容器)也可以是一个类型:

  比如:

template<typename T>
class MyClass
{
    typename T::SubType* ptr;
}

  这里介绍模板内参数名称的几个概念;

  从属名称(dependent names): 模板(template)内出现的名称, 依赖于某个模板(template)参数, 如T t;

  嵌套从属名称(nested dependent names):从属名称在class内呈嵌套装, 如T::const_iterator ci;

  非从属名称(non-dependent names): 不依赖任何template参数的名称, 如int value;
  
  任何时候在模板(template)中指涉一个嵌套从属类型名称, 需要在前一个位置, 添加关键字typename;

  如果不特定指出typename, 嵌套从属名称, 有可能产生解析(parse)歧义,可能会报错(GCC): error: need 'typename' before 'T::xxx' because 'T' is a dependent scope。

  比如:

template<typename T>
class MyClass
{
    /*typename*/ T::SubType* ptr;
}

  上述程序中,第二个typename被用来说明,SubType是定义与类T内部的一种类型,也就是associated type,因而,ptr是一个指向T::SubType类型的指针。如果不使用typename,T::SubType会被优先看做T的一个静态成员,也就是一个具体而变量或对象,于是,下面的表达式:

T::SubType* ptr;

  编译器此时就无法辨别这SubType是什么,因为SubType可能是模板参数T内的一个static变量,ptr可以看成一个全局变量,此时代码会被看做类T的静态成员SubType和ptr的乘积,或者SubType可能是一个typedef比如

class Class_T{
    typedef int SubType;
    ...
};

  那上面代码转化过来就是这样:

int *x;

 

扫描二维码关注公众号,回复: 6438429 查看本文章

2、嵌套从属名称使用typename的几个场景

  a、模板内出现的名称如果依赖于某个模板参数,称之为从属名称(dependent name)。如果从属名称在class内呈嵌套状,我们称为嵌套从属名称(nested dependent name),举例如下:

template<typename T>
void print(const T & container)
{
    T::const_iterator iter(container.begin());
    cout << *iter << endl;
    int value = *iter;
    return;
}
  • 在上述代码中,iter 的类型是依赖于模板参数T的,因此被称为 从属名称;
  • 同理,value的类型是语言内置类型,不依赖于任何模板参数,因此被称为 非从属名称;
  • C++编译器在面对从属名称时,如果此时该从属名称又嵌套了其他类型,如此处的 iter就是T::const_iterator类型,这里的T::const_iterator 称为嵌套从属类型名称(嵌套于T类型,从属于模板参数T)

 或者

template<typename T> // typename allowed (as is "class")
void f(const T& container, // typename not allowed typename T::iterator iter); // typename required

  上述的T并不是嵌套从属类型名称 (它并非嵌套与任何“取决于模板参数”的东西内),所以声明container时并不需要以typename为前导。
但T::iterator是个嵌套从属类型名称,所以必须以typename为前导。
  

  b、某个模板类里面typedef类型时,如果这个类型与模板参数T相关,就需要使用typename。即这样的形式:

template <class T>
class Test
{
public:
 typedef map<int, T> TEMPLATE_MAP; //TEMPLATE_MAP不需要typename,因为它不依赖其他的名称
 typedef map<int, T>::iterator TEMPLATE_MAP_ITER;  //error! 嵌套从属名称,依赖其他类的iterator名称
 typedef typename map<int, T>::iterator TEMPLATE_MAP_ITER; //yes 
};

 

   c、例外:嵌套从属类型名称, 如果是基类列表(base class list)和成员初值列(member initialization list)中,不使用typename;

/*
 * BInsertSort.cpp
 *
 *  Created on: 2014.4.17
 *      Author: Spike
 */
 
#include <iostream>
#include <vector>
 
using namespace std;
 
struct Number {
    Number(int x) {
        std::cout << "Number = " << x << std::endl;
    }
};
 
template<typename T>
struct Base{
    typedef Number Nested;
};
 
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(7); //必须使用
    }
};
 
int main () {
    Derived<int> d(5);
 
    return 0;
}

 

猜你喜欢

转载自www.cnblogs.com/kerngeeksund/p/10700747.html