随笔 - 55  文章 - 15  trackbacks - 0
<2012年5月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

常用链接

留言簿

随笔分类

随笔档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜

     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<floatint>().f();// Primary tempalte
  C<intfloat>().f();// T==int
  C<int*, float*>().f();// T* and U* used , T = int, U = float

  C<intint>().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 voidconst& 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)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理