C++ Templates:技巧性基础知识(来自《C++ template》)

关键字typename:
引入关键字typename是为了说明:模板内部的标识符可以是一个类型:

   1:  template <typename T>
   2:  class Myclass {
   3:      typename T::SubType *ptr;
   4:  }

.template构造:

   1:  void printBitset (std::bitset<N> const& bs) {
   2:      std::cout<< bs.template to_string< char, char_traits<char>, allocator<char> >();
   3:  }

.template告诉编译器.template后面的小于号是模板实参列表的起始符号。

使用this->:
对于那些在基类中声明,并且依赖于模板参数的符号(函数或变量等),应该在它们前面使用this->或者Base<T>::。
如果希望完全避免不确定性,可以限定模板中所有的成员访问。

成员模板和模板的模板参数:
通常,栈之间只有在类型完全相同时才能相互赋值,其中类型指的是元素的类型,即使这两种元素的类型之间存在隐式类型转换。‘
函数模板不支持模板的模板参数。
不同类型间赋值的解决办法(并加入模板的模板参数的完整版):

   1:  #include <deque>  
   2:  #include <stdexcept>  
   3:  #include <memory>  
   4:  template <typename T,   
   5:      template <typename ELEM, typename = std::allocator<ELEM> > class CONT = std::deque>  //模板的模板参数,其中类的两个模板参数是为了匹配std::deque的两个模板实参缺省模板参数(元素类型和内存分配器allocator)  
   6:  class Stack {  
   7:  private:  
   8:      CONT<T> elems;         // 由T来决定类模板中的元素的类型,实例化时可直接写为:Stack<int, std::vector> vStack  
   9:  public:  
  10:      void push(T const&);   // push element  
  11:      void pop();            // pop element  
  12:      T top() const;         // return top element  
  13:      bool empty() const {   // return whether the stack is empty  
  14:          return elems.empty();  
  15:      }  
  16:      //在元素不同的栈之间相互赋值的重载operator=  
  17:      template<typename T2,   
  18:          template<typename ELEM2, typename = std::allocator<ELEM2> >class CONT2>  
  19:      Stack<T,CONT>& operator= (Stack<T2,CONT2> const&);  
  20:  };  
  21:  template <typename T, template <typename,typename> class CONT>  
  22:  void Stack<T,CONT>::push (T const& elem)  
  23:  {  
  24:      elems.push_back(elem);    // append copy of passed elem  
  25:  }  
  26:  template<typename T, template <typename,typename> class CONT>  
  27:  void Stack<T,CONT>::pop ()  
  28:  {  
  29:      if (elems.empty()) {  
  30:          throw std::out_of_range("Stack<>::pop(): empty stack");  
  31:      }  
  32:      elems.pop_back();         // remove last element  
  33:  }  
  34:  template <typename T, template <typename,typename> class CONT>  
  35:  T Stack<T,CONT>::top () const 
  36:  {  
  37:      if (elems.empty()) {  
  38:          throw std::out_of_range("Stack<>::top(): empty stack");  
  39:      }  
  40:      return elems.back();      // return copy of last element  
  41:  }  
  42:  //在元素不同的栈之间相互赋值的重载operator=  
  43:  template <typename T, template <typename,typename> class CONT>  
  44:      template <typename T2, template <typename,typename> class CONT2>  
  45:  Stack<T,CONT>& Stack<T,CONT>::operator= (Stack<T2,CONT2> const& op2)  
  46:  {  
  47:      if ((void*)this == (void*)&op2) {    // 自身赋值检测  
  48:          return *this;  
  49:      }  
  50:      Stack<T2,CONT2> tmp(op2);        // 创建一份赋值栈的拷贝,以便使用top()和pop()从该拷贝中获取元素。  
  51:      elems.clear();                   // 移除现有元素  
  52:      while (!tmp.empty()) {           // 拷贝所有元素  
  53:          elems.push_front(tmp.top());  
  54:          tmp.pop();  
  55:      }  
  56:      return *this;  
  57:  } 
 

零初始化:
对于int、double或者指针等基本类型,应该显式地调用内建类型的缺省构造函数,并把缺省值设为0(或者false,对于bool类型而言)。比如调用int()我们就将获得缺省值0。

   1:  template <typename T>
   2:  void foo() {
   3:      T x = T() ;
   4:  }

对于类模板,需要定义一个缺省构造函数,通过一个初始化列表来初始化类模板的成员。

   1:  template <typename T>
   2:  class Myclass {
   3:  private:
   4:      T x;
   5:  public:
   6:      Myclass() : x()
   7:      ...
   8:  };

使用字符串作为函数模板的实参:
如果把字符串传递给函数模板的引用参数时,由于长度的区别,长度不同的字符串属于不同的数组类型,可以通过传递给非引用类型的参数来解决这个问题,但对于非引用类型的参数,在实参演绎的过程中,会出现数组到指针的类型转换。

根据不同的情况,对此类问题的解决方法:

使用非引用参数,取代引用参数(然而,可能会导致无用的拷贝)
进行重载,编写接收引用参数和非引用参数的两个重载函数(然而,可能会导致二义性)
对具体类型进行重载(比如对std::string进行重载)
重载数组类型,如:
    template <typename T, int N, int M>
    T const* max(T const (&a)[N], T const (&b)[M]) {
        return a < b ? b : a;
    }

强制要求应用程序程序员使用显式类型转换。

小结:

如果要访问依赖于模板参数的类型名称,应该在类型名称前添加关键字typename。
嵌套类和成员函数也可以是模板。
类模板也可以作为模板参数,称之为模板的模板参数。
模板的模板实参必须精确地匹配。
通过显式调用缺省构造函数,可以确保模板的变量和成员都已经用一个缺省值完成初始化,这种方法对内建类型的变量和成员也适用。
对于字符串,在实参演绎过程中,当且仅当参数不是引用时,才会出现数组到指针的类型转换

posted @ 2011-04-26 16:50  云端小飞象cg  阅读(714)  评论(0编辑  收藏  举报