1. typename
template <typename T>
class MyClass {
typename T::SubType * ptr;
…
};
如果不使用typename,SubType会被认为是一个静态成员,T::SubType就会被解释成两个变量的乘积。
typename的典型应用: STL容器的iterator
// basics/printcoll.hpp
#include <iostream>
// print elements of an STL container
template <typename T>
void printcoll (T const& coll)
{
typename T::const_iterator pos; // iterator to iterate over coll
typename T::const_iterator end(coll.end()); // end position
for (pos=coll.begin(); pos!=end; ++pos) {
std::cout << *pos << ' ';
}
std::cout << std::endl;
}
class stlcontainer {
…
typedef … iterator; // iterator for read/write access
typedef … const_iterator; // iterator for read access
…
};
2. this->
下面的例子中foo()调用exit(),它不会调用父类Base的exit(),而是会调用外部定义的exit()。如果要调用父类的exit()就需要加上this->或者Base<T>::
template <typename T>
class Base {
public:
void exit();
};
template <typename T>
class Derived : Base<T> {
public:
void foo() {
exit(); // calls external exit() or error
}
};
3. 成员模板(Member Templates)
我们有这样的需求:把两个不同模板类型的类相互赋值。
Stack<int> intStack1, intStack2; // stacks for ints
Stack<float> floatStack; // stack for floats
…
intStack1 = intStack2; // OK: stacks have same type
floatStack = intStack1; // ERROR: stacks have different types
这里我们就需要使用成员模板
// basics/stack5decl.hpp
template <typename T>
class Stack {
private:
std::deque<T> elems; // elements
public:
void push(T const&); // push element
void pop(); // pop element
T top() const; // return top element
bool empty() const { // return whether the stack is empty
return elems.empty();
}
// assign stack of elements of type T2
template <typename T2>
Stack<T>& operator= (Stack<T2> const&);
};
operator=的实现:
// basics/stack5assign.hpp
template <typename T>
template <typename T2>
Stack<T>& Stack<T>::operator= (Stack<T2> const& op2)
{
if ((void*)this == (void*)&op2) { // assignment to itself?
return *this;
}
Stack<T2> tmp(op2); // create a copy of the assigned stack
elems.clear(); // remove existing elements
while (!tmp.empty()) { // copy all elements
elems.push_front(tmp.top());
tmp.pop();
}
return *this;
}
4. 模板的模板参数(Template Template Parameters)
当我们要指定一个Stack模板类型时,要这样写:
Stack<int,std::vector<int> > vStack; // integer stack that uses a vector
这样有些繁琐int出现了两次,我们希望的是:
Stack<int,std::vector> vStack; // integer stack that uses a vector
此时就要用到模板的模板参数
// basics/stack7decl.hpp
template <typename T,
template <typename ELEM> class CONT = std::deque >
class Stack {
private:
CONT<T> elems; // elements
public:
void push(T const&); // push element
void pop(); // pop element
T top() const; // return top element
bool empty() const { // return whether the stack is empty
return elems.empty();
}
};
这里有几点需要注意的是:
1)template <typename ELEM> class CONT = std::deque中的class不能换成typename
2)模板的模板参数不能用于函数模板
3)根据上述的默认值std::deque会得到一个编译错误:
error C3201: the template parameter list for class template 'std::deque' does not match the template parameter list for template parameter 'CONT'
发生这个错误的原因是:
std::deque除了ELEM这个模板参数之外,还有另一个allocator的参数:typename ALLOC = std::allocator<ELEM>
在模板编译的时候要求模板定义的参数CONT与模板的实参完全一致,我们需要将allocator参数加上:
template <typename T,
template <typename ELEM,
typename ALLOC = std::allocator<ELEM> >
class CONT = std::deque>
class Stack {
private:
CONT<T> elems; // elements
…
};
5. 初始化为0(Zero Initialization)
这里要解决的问题是如何将模板类型的变量初始化
template <typename T>
void foo()
{
T x = T(); // x is zero (or false)ifT is a built-in type
}
template <typename T>
class MyClass {
private:
T x;
public:
MyClass() : x() { // ensures that x is initialized even for built-in types
}
…
};
上面分别介绍了对函数内的局部变量和类变量的初始化。
6. 字符串作为函数模板的实参
// basics/max5.cpp
#include <string>
// note: reference parameters
template <typename T>
inline T const& max (T const& a, T const& b)
{
return a < b ? b : a;
}
int main()
{
std::string s;
::max("apple","peach"); // OK: same type
::max("apple","tomato"); // ERROR: different types
::max("apple",s); // ERROR: different types
}
这里发生编译错误的原因是"apple”和"tomato”的类型分别是char const[6]和char const[7]。
最好的解决方法就是为字符串重载max()
7. 模板源代码组织
(1)包含模型
把模板函数和模板类的实现放在头文件中,这个方法是相对来说最简单,最推荐的方法。
(2)显式实例化
手工对需要的模板类型进行实例化,而不依赖编译器。这种做法的好处就是可以把模板函数的声明和定义分开,并且可以精确控制模板实例的准确位置,坏处就是需要人工的跟踪每个需要实例化的实体,不适合用于大项目。
包含模型和显式实例化整合
(3) 分离模型
分离模型就是通过使用export关键字来实现模板声明和定义的分离,很完美?不过它有一个非常重大的缺陷:
它会产生看不见的代码耦合(模板的声明和定义在不同文件中),当定义发生变化时,所有调用该模板函数的地方都需要重编译,而这些对一些编译工具来说是不可见的,比如Makefile。所以不推荐使用。
分离和包含模型的转换
// basics/myfirst4.hpp
#ifndef MYFIRST_HPP
#define MYFIRST_HPP
// use export if USE_EXPORT is defined
#if defined(USE_EXPORT)
#define EXPORT export
#else
#define EXPORT
#endif
// declaration of template
EXPORT
template <typename T>
void print_typeof (T const&);
// include definition if USE_EXPORT is not defined
#if !defined(USE_EXPORT)
#include "myfirst.cpp"
#endif
#endif // MYFIRST_HPP
// use separation model:
#define USE_EXPORT
#include "myfirst.hpp"
…
// use inclusion model:
#include "myfirst.hpp"