Thinking in C++ 第二卷第五章的内容,先从模板的特化说起。模板的内容太丰富了,学习是一回事,要掌握模板肯定要在工作中经常使用。
模板特化我们知道,模板描述了一族类或者一族函数。实例化一个模板类,就是在这一族类中拿出特定的一种。特化就是万中取一的过程。
显式特化
自己提供代码,而不是在实例化的时候特化。
例如:
template<class T, class Allocator = allocator<T> >
class vector{};
template<> class vector<bool, allocator<bool> > {..};
template<>明确告诉编译器这是一个模板的特化。
在特化一个类模板的时候,一般要实现其中的所有成员函数。由于所提供的时一个单独的类,客户代码常常希望能提供完整的接口实现。
半特化(偏特化)
没有完全特化成特定的类型,特化的不完全体,还保留其他可能性。例:
template<class Allocator>class vector<bool, Allocator>{};
用户可以提供一个自定义的allocator。
选择哪个类模板来进行实例化的规则遵循“特化程度最高”的原则(半有序)。例:
template<class T, class U>
class X
{
public:
void f(){ cout << " Primary Template" << endl;}
};
template< class U>
class X<int, U>
{
public:
void f(){ cout << " T==int" << endl;}
};
template< class U>
class X<int, U>
{
public:
void f(){ cout << " T==int" << endl;}
};
template<class T>
class X<T,T>
{
public:
void f(){ cout << "T==U" << endl;}
};
int main()
{
C<float, int>().f();// Primary tempalte
C<int, float>().f();// T==int
C<int*, float*>().f();// T* and U* used , T = int, U = float
C<int, int>().f();//Duplicate X<int,U>, X<T>
return 0;
}
即,在特化或者偏特化的时候不要让编译器为难,尽量避免二义性的出现。
ps:如果类参数符合偏特化,就调用偏特化, 如果符合几个偏特化,出现二义性,编译错误
防止代码膨胀
一旦对某个类模板进行了实例化, 伴随着所有在程序中调用的该模板的成员函数,类定义中用于对其进行详尽描述的特化代码也会产生。只有被调用的成员函数才产生代码。例:
class X
{
public:
void f(){}
};
class Y
{
public:
void g(){}
};
template<class T> class Z
{
T t;
public:
void a(){ t.f();}
void b(){ t.g();}
};
int main()
{
Z<X> zx;
zx.a();//Doesn't create Z<X>::b()
Z<Y> zy;
zy.b();//Doesn't create Z<Y>::a()
return 0;
}
但是虽然我们在代码中只写了一份,但是当你要将该类模板实例化为int型,void型和其他类型的时候,你会发现,编译器在后台为你复制粘贴了一系列相应的代码,这使我们的代码膨胀。解决方法是用void*进行完全特化,然后从void* 实现中派生出所有其他的指针类型。
template<
class T>
class Stack
{
T* data;
public:
void push(
const T& t){
}
void pop() {
}
};
template<>
class Stack<
void*>
{
void** data;
public:
void push(
const void*
const& t){
}
void pop(){
};
};
template<
class T> |
class Stack<T*> :
private Stack<
void*> |
{ |
typedef Stack<
void*> Base; |我理解是只产生这么一小段代码,因为Base是已经特化好了的,所以Base的代码只有定义一次。
public: |如果有个int*的实例的话,那就只要调用Base就行了,不必要再为int* 再生次一次代码。
void push(T*
const& t) { Base::push(t);} |
void pop(){ Base::pop();} |
}; |
名字查找问题
当编译器首次看到一个模板定义的时候,它不知道有关这个模板的任何信息,只有当它看到模板的实例化时,才能判断这个模板是否被正确地使用了。 这种情况导致模板的编译分两个阶段进行。
第一阶段: 解析模板定义,寻找明显的语法错误,解析所有能解析的符号。有些依赖与模板参数的符号就不能解析了。
第二阶段: 编译器决定是否用模板的一个显式特化代替基本的模板。
123
posted on 2012-05-08 19:23
Dino-Tech 阅读(248)
评论(0) 编辑 收藏 引用