C++(八)——typename用法

  • Post author:
  • Post category:其他




1、typename来源



1.1原因1:

从表面上看,下面模板的参数只支持用户自定义类型,但其实对语言内置类型或者指针调用也支持:

template <class T>
int compare(const T &v1, const T &v2)
{
    if (v1 < v2) return -1;
    if (v2 < v1) return 1;
    return 0;
}
//用指针调用:
int v1=1 ,v2=2;
int ret= cmpare(v1, v2)

class在类和模板中的表现的意义存在一些不一致。前者针对用户自定义类型,后者包含了语言内置类型和制止呢。



1.2真实原因:



1.2.1一些关键概念:


限定名:限定了命名空间的名称。

std::cout; //std就是一个命名空间R


依赖名和非依赖名


依赖名是指依赖于模板参数的名称,而非依赖名则相反,指不依赖于模板参数的名称。

template<class T>
class Myclass
{
	int i;
	vector<int> vi;
	vector<int>::iterator vitr;  //非依赖名
	T t;
	vector<T> vt;
	vector<T>::iterator viter;  //依赖名
	//依赖于模板参数的名称就是依赖名
}


类作用域


在类外部访问类中的名字时,可以使用类作用域符,形如class::name的调用通常有三种:静态数据成员、静态成员函数和嵌套类型。

struct Myclass
{
static int A;  //静态数据成员
static int B(); //静态函数成员
typedef int C; //嵌套类型
}


1.2.2真实原因
template<class T>
void foo()
{T::iterator * iter;
}

T::iterator 这样的一个定义可以是以上三种类作用域中的任意一种类型。

(1)如果iterator是嵌套类型将正确执行,这段代码的意思是定义一个 T::iterator类型的数据。

(2)当iterator是静态数据成员时,以上代码将被解释为两个数相乘,返回值抛弃。如果iter没有定义,将报错;但如果iter是全局变量,将执行。

在模板实例化之前完全没有办法区分,因此,有必要引入新的关键字typename来区分这两种情况。



2、typename用法



2.1 C++标准

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

即,当你想告知编译器iterator是类型而不是变量,只需要用typename:

template<class T>
void foo()
{typename T::iterator * iter;
}

这时,编译器可以确定T::iterator是一个类型,而不需要等到实例化时再确定。



2.2使用typename的规则

typename在下面情况禁用:

模板定义之外,即typename只能用于模板中。

非限定类型,比如前面介绍过的int,vector之类。

基类列表中,比如template class C1: T::TnnerType

构造函数的初始化列表中

如果类型是依赖于模板参数的限定名,那么在它之前必须加typename(除非在类的初始化成员表中)

在其他情况下,typename是可选的,也就是说对于一个不是依赖名的限定名,该名称可选,比如vector vi;



2.3其他情况

对于不引起歧义的情况,仍然需要加typename,比如:

template<class T>
void foo(){
typename T::iterator_type iterator_type;
typedef typename _type_traits_<T>::iterator iterator_type;}//将_type_traits_<T>这个模板类中的iterator重命名为 iterator_type



2.4其他说明

对于制定模板参数这一用法,class和typename都支持,但typename更能准确表达含义。



版权声明:本文为iotflh原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。