typename
在C++标准化工程中,引入关键字typename是为了说明:模板内部的标识符可以是一个类型。比如下面的例子:
template <typename T>
class MyClass{
typename T::SubType *ptr;
// ...
};
上面程序中,第2个typename被用来说明:SubType是定义于类T内部的一种类型。因此,ptr是一个指向T::SubType类型的指针。
如果不使用typename,SubType就会被认为是一个静态成员,那么它应该是一个具体的变量或者对象,于是,表达式T::SubType *ptr
会被看成是类T的静态成员SubType和ptr的乘积
通常而言,当某个依赖于模板参数的名称是一个类型时,就应该使用typename
我们来看一个typename的典型应用,记载模板代码中访问STL容器的迭代器:
#include <iostream>
template<typename T>
void printcoll(T const & coll){
typename T::const_iterator pos;
typename T::const_iterator end(coll.end());
for(pos = coll.begin(); pos != end; ++pos){
std::cout << *pos << "\t";
}
std::cout << std::endl;
}
在这里函数模板中,调用参数是一个T类型的STL容器。为了迭代容器中的所有元素,我们借助于迭代器类型。而在每个ST;容器类中,都声明有迭代器类型const_iterator:
class stlcontainer{
typedef ... iterator; // 读写迭代器
typedef ... const_iterator; // 只读迭代器
};
因此,为了访问到的类型为T的const_iterator类型,你需要在声明开始出使用关键字typename来加以限定:
typename T::const_iterator pos;
.template构造
我们在引入typename之后,发现了一个很相似的问题。考虑下面这个使用标准bitset类型的例子:
#include <iostream>
#include <bitset>
template<int N>
void printfBitSet(std::bitset<N>const & bs){
std::cout << bs.template to_string<char, std::char_traits<char>, std::allocator<char>>();
}
上例中有一个奇怪的构造:.template
。如果没有使用这个template,编译器将不知道下列事实:bs.template后面的小于号<
并不是数学中的<
,而是模板实参列表中的起始符合;那么只有在编辑器判断<
之前,存在依赖于模板参数的构造,才会出现这种问题。在这个例子中,传入参数bs就是依赖于模板参数N的构造。
总是,只有在该前面存在依赖于模板参数的对象是,我们才需要在模板内部使用,使用.t emplate/->template
标记,而且这个标记也只能在模板中使用。
总结
- 作为类型前的标志符号
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;
template<class T>
class MyClass {
typename T::SubType * ptr;
/*typename指出SubType是T中定义的一个类型,因此ptr是一个指向T::SubType的指针*/
//T::SubType * ptr1;如果不加typename,表达式被认为是T的静态成员SubType和ptr的乘积
};
void main()
{
system("pause");
}
-
C++中的一般规则是,除了以typename修饰以外,template内的任何标识符合都被视为一个值value,而非一个类型。
-
typename的第二个作用:在模板声明中替换关键字class。
template<typename T>class MyClass;