- 关键词typename的另一种用途
- 将成员函数和嵌套类作为模板的模板模板参数template template paramters
- 零值初始化zero Initialization
- 字符串字面常量string literals作为模板参数
1. 关键词 typename
关键词typename是C++标准化过程中引入的,目的在向编译器说明template内的某个标识符是类型(而不是其他)。考虑如下例子:
template
class MyClass {
typename T::SubType * ptr;
...
};
在这里,第二个typename关键词的意思是SubType是class T内部定义的一个类型,从而ptr是一个
“指向T::SubType”的指针。
如果上面没有使用关键词typename,SubType会被认为是class T的一个static成员,于是编译器理解一个具体变量或一个对象,导致下面的语句:
T::SubType * ptr;
表达的意义变成:class T的一个静态static成员SubType与ptr相乘。
通常如果某个与template parameter相关的名称是个类型type时,就必须加上关键词typename。
1.1 迭代器应用
typename的一个典型应用是在template程序代码中使用STL容器供应的迭代器iterators:
#include
template
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 << ' ';
}
std::cout << std::endl;
}
在这个函数模板中, coll是个STL容器,其元素类型为T。这里使用了STL容器的迭代器类型iteration type遍历coll中的所有元素。迭代器类型为const_iterator,每个STL容器都声明有这个类型:
class stlcontainer {
...
typedef ... iterator;
typedef ... const_iterator;
...
};
使用模板类型template Type T的const_iterator时,必须写出全民并在最前面加上关键词typename:
typename T::const_iterator pos;
1.2 .template 结构体(construct)
考虑如下程序,其中使用标准的bitset类型:
template
void printBitset(std::bitset
{
std::cout << bs.template to_string
}
此例子中的 .template看起来古怪,但是如果没有它,编译器无法得知紧跟其后面的 “<”是template argument list的起始,而非一个小于号。这里的参数bs依赖于模板参数template paramter N。
结论是“.template” 或"->template"记号只在templates之内才能被使用,且必须紧跟着与模板参数相关的东西。
2 .使用this指针
如果class template拥有base class,那么其内出现的成员名称x并非总是等价与this->x,即使x系继承而来。例如:
template
class Base {
public:
void exit();
};
template
class Derived: public Base
public:
void foo() {
exit();
}
};
本例中,foo()内解析exit符号时,定义于Base的exit会被编译器忽略。因此,要么获得一个编译器错误,要么是调用外部额外写的exit()函数。
目前避免这样的情况有一个准则:
使用与template相关的符号时,建议总是以this->或Base
3.成员模板Member Templates
class的成员既可以是templates:既可以是nested class templates,也可以是member function templates。通常只有当两个stack类型相同,也就是当两个stacks拥有相同类型的元素时,你才能对它们相互赋值assign,也就是将某个stack赋值给另一个。不能把某种类型的stack赋值给另一种类型的stack,即使这两种类型之间可以隐式转型:
Stack
Stack
...
intStack1 = intStack2; //OK
floatStack = intStack1; //ERROR: 两个stacks类型不同
然而,可以把assignment运算符定义为一个template,就可以把两个“类型不同,但其元素可以隐式转型的”堆栈互相赋值。为完成此时,Stack<>需要如下面声明:
template
class Stack {
private:
std::deque
public:
void push(T const &);
void pop();
T top() const;
bool empty() const {
return elems.empty();
}
template
Stack
}
相比原先的Stack,有如下改动:
1)增加一个assignment运算符,使Stack可被赋予一个拥有不同元素类型T2的Stack.
2)这个Stack使用Deque作为内部容器。
具体新增加的assignment运算符定义实现如下:
template
template
Stack
{
if((void*)this == (void*) &op2) {
return *this;
}
Stack
elems.clear();
while(!tmp.empty()){
elems.push_front(tmp.top());
tmp.pop();
}
return *this;
}
在拥有template parameter T的模板中定义一个内层的inner模板参数template parameter T2:
template
template
...
现在有了这个赋值模板成员member template,就可以把一个int stack赋值给一个float stack:
Stack
Stack
...
floatStack = intStack1;
这看起来绕过了类型检查,其实必要的类型检查还是有的,发生在源端的元素拷贝到目的端时进行:elems.push_front(tmp.top());
这里会将整数类型的元素转换成浮点类型元素。
因此试图将字符堆栈转换成浮点堆栈是不能成功的,因为string不能转换成float类型。
前面的模板赋值运算符并不取代default 赋值运算符。如果你在相同类型的Stack之间赋值,编译器还是会采用default assignment运算符。
这里,我们也可以把内部容器参数化:
template
Class Stack{
private:
CONT elems;
public:
void push(T const&);
void pop();
T top() const;
bool empty() const {
return elems.empty();
}
template
Stack
};
此时,template assignment运算符实现如下:
template
template
Stack
{
if((void*)this == (void*)&op2) {
return *this;
}
Stack
elems.clear();
while(!tmp.empty()) {
elems.push_front(tmp.top());
tmp.pop();
}
return *this;
}
4. 模板模板参数(双模板参数)Template Template parameters
一个模板参数template parameter本身也可以是个类模板class template,这就是(类)模板(的)模板参数的由来,类模板的模板参数非常有用。
为了使用其他类型的元素容器,stack class使用者必须两次知道元素类型,一次是元素类型本身,另一次是容器类型:
Stack
但是如果使用模板模板参数,就可以仅仅指明元素类型,无须再指定容器类型:
Stack
为了实现这种特性,你必须把第二个模板参数template parameter声明为模板(的)模板参数。
原则上代码可以写为:
template template Class Stack { private: CONT public: void push(T const&); void pop(); T top() const; bool empty() const { return elems.empty(); } }; 与先前的差别是,第二个模板参数被声明为一个类模板class template,上面黑体部分, template 其默认值也由原先的std::deque CONT 但是这样编译还是有问题,这就是: 模板模板参数的匹配问题。 本例的问题在于:标准库中的std::deque template要求不只是一个参数:第二个参数是个配置器allocator,它虽然有默认值,但当它被用来匹配CONT的参数时,其默认值就被编译器强行忽略了。我们可以重写class声明语句。使得CONT参数要求带两个参数的容器: template template typename ALLOC = std::allocator class CONT = std::deque> Class Stack { private: CONT ... }; 最后的Stack template实现如下。 1)stack.hpp 2) stacktest.cpp 5. 零值初始化 6. 字符串常数作为模板参数 7. 小结



