C7510 use of dependent type name must be prefixed with ‘typename‘

Error C7510 ‘iterator’: use of dependent type name must be prefixed with ‘typename’

//报错代码
template <typename  T>
void myprint(T& v) {
	T::iterator it = v.begin();	 //error  c7510 occur
	for (; it != v.end(); it++)
	{
		std::cout << *it << std::endl;
	}
}
//改为如下
typename T::iterator it = v.begin();	//告诉编译器	T::iterator是类型而不是变量
分析

我想定义一个指针it,指向的类型是包含在类作用域T中的iterator

//可能存在包含iterator类型的结构
struct AClass{
    struct iterator{
        //...
    }
}
//如此实例化
myprint<aClass>();
问题

限定名和非限定名

限定名(qualified name),故名思义,是限定了命名空间的名称。看下面这段代码,coutendl就是限定名:

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

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

如果在上面这段代码中,前面用using std::cout;或者using namespace std;,然后使用时只用coutendl,它们的前面不再有空间限定std::,所以此时的coutendl就叫做非限定名(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<T>vector<T>::iterator称为依赖名。前三个定义叫做非依赖名。

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

类作用域

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

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

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

通过前面类作用域一节的介绍,我们可以知道,T::iterator实际上可以是以下三种中的任何一种类型:

  • 静态数据成员
  • 静态成员函数
  • 嵌套类型

前面例子中的AClass::iterator是嵌套类型,完全没有问题。可如果是静态数据成员呢?如果实例化foo模板函数的类型是像这样的:

struct BClass {    static int iterator;    // ...};

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

myprint<BClass>(new Bclass);

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

error C2065: ‘iter’ : undeclared identifier

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

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

使用typename的规则

最后这个规则看起来有些复杂,可以参考MSDN

  • typename在下面情况下禁止使用
    • 模板定义之外,即typename只能用于模板的定义中
    • 非限定类型,比如前面介绍过的intvector<int>之类
    • 基类列表中,比如template <class T> class C1 : T::InnerType不能在T::InnerType前面加typename
    • 构造函数的初始化列表中
  • 如果类型是依赖于模板参数的限定名,那么在它之前必须加typename(除非是基类列表,或者在类的初始化成员列表中)
  • 其它情况下typename是可选的,也就是说对于一个不是依赖名的限定名,该名称是可选的,例如vector<int> vi;
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值