Templates使用技巧基础

模板使用技巧基础

这篇文章主要记下实际工程中使用模板的一些基本技巧,其中主要包括typename关键字的使用,定义模板成员函数,定义嵌套类模板,双重模板类型参数(template template parameter),以及零值初始化的技巧。

 

一、关键字typename

关键字typename是C++标准化过程中被引入的,目的是告诉编译器模板类型参数或者其内的某个标识符是个类型。

template <typename T>
class MyClass{
public:
   typename T::subtype *ptr;
   ... ...
};

 第二个typename关键字说明subtype是T内部定义的一个类型,所以ptr是一个指向subtype的指针。如果移除掉typename关键字修饰,那么subtype会被编译器误认为是T内部的一个静态成员,所以 T::subtype * ptr 就变成了一个表达式。

 

通常如果某个与模板参数相关的名称是个类型时,前面就需要添加关键字typename.

二、.template构件

在引入typename之后,又出现了一个类似问题。如下面的代码所示

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

 .template构件显得比较古怪,如果没有它,编译器不会认为紧跟其后的‘<’ 是模板参数列表的开始,而非一个小于号。

 

小结:.template构件只有在其前的构件(本例是bs)依赖于模板类型参数时,才会被使用。

 

三、使用this->

template <typename T> 
class Base { 
  public: 
    void exit(); 
};

template <typename T> 
class Derived : Base<T> { 
  public: 
    void foo() { 
        exit();   // calls external exit() or error 
    } 
}; 

 如果class templates拥有base classes,那么其出现的成员名称'exit()'并非总是等价于‘this->exit()',即base<T>::exit()。应在调用前,显式添加’this->' 或‘base<T>::'才可通过编译。

 

使用建议准则:任何使用基类继承而来的成员或成员函数,如果基类与模板类型参数相关,那么最好在使用前加上'this->'或者’base<T>::'的修饰。

 

四、成员模板

成员模板可以是嵌套模板类也可以是成员函数模板。

// basics/stack6decl.hpp 

template <typename T, typename CONT = std::deque<T> > 
class Stack { 
  private: 
    CONT 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 T, typename CONT2> 
    Stack<T,CONT>& operator= (Stack<T,CONT2> const&); 
}; 


// basics/stack6assign.hpp 

template <typename T, typename CONT> 
 template <typename T, typename CONT2> 
Stack<T,CONT>& 
Stack<T,CONT>::operator= (Stack<T,CONT2> const& op2) 
{ 
    if ((void*)this == (void*)&op2) {    // assignment to itself? 
        return *this; 
    } 

    Stack<T> 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; 
}

 通过上述成员赋值模板函数,就可完成以deque实现的stack赋值到以vector实现的stack,不过stack中保存的元素类型仍然相同。

 

五、双重模板类型参数(Template Template Parameter)

template <typename T, typename CONT = std::deque<T> > 
class Stack { 
...
};

如此声明,在使用不同container作为stack的实现时,需要再次重复指定元素类型。如果使用ttp的话,那么就可以不需要重复指定元素类型了。那么ttp到底是个什么东东呢?实际上ttp就是在声明时告诉编译器模板类型参数本身也是一个模板类。按照ttp原则,这个例子中,stack的container模板类型参数本身也是一个类模板。所以修改如下

template<
                typename T, 
                template<typename ELEM>class CONT=std::deque 
              >
class Stack { 
... ...
};

如果按照ttp声明stack模板类时,那么在使用不同container作为stack的实现时,就不再需要重复指定元素类型了。

 

技巧1:因为TTP中的模板类型参数(ELEM)并没有被使用,所以形式上可以忽略。所以下面的这个声明也可以

template<
                typename T, 
                template<typename>class CONT=std::deque 
              >
class Stack { 
... ...
};
 

注意事项:

      成员函数模板的声明及实现中的模板类型参数也需要按照TTP原则做出相同的改动。

模板模板参数的匹配(Template Template Argument Matching)

如果试图使用按照TTP原则修改后的class templates stack,编译器会报错说std::deque不符合template template parameter CONT的要求,原因是TTP原则不仅要求template parameter本身是个class template,而且这个class template的参数必须严格匹配它所替换的TTP的参数。

 

再次考虑本例,原本代表container的那个template parameter是 std::deque<T, alloc<T> >, 很显然这个container是需要两个template parameter的,但是当我们之前用以替换的class template却只声明了一个template parameter,这就导致了参数匹配不成功。所以需要进一步修改一下class template Stack的声明如下

 

template <typename T, 
          template <typename ELEM, typename ALLOC = std::allocator<ELEM> > 
                    class CONT = std::deque> 
class Stack { 
  private: 
    CONT<T> elems;         // elements 
    … 
}; 

 PS: 似乎为了少写一个T, 却使class template声明变得很复杂。

六、零值初始化

template <typename T> 
void foo() 
{ 
    T x = T();      // x has undefined value if T is built-in type 
} 

假定T并无默认构造函数时,x的初值就无法被正确初始化,进而引发未定义的行为。因此在定义class template时,一定要定义默认构造函数,初始化其内部成员。当然,如果T本身是内建类型时,如int, bool等, 当默认构造函数被调用后,其值就会被初始化为0。

 

小结:

 1. 当要操作一个取决于template parameter的类型名称时,应该在前面加typename关键字修饰。

 2. 嵌套类和成员函数也可以是模板。

 3. 赋值运算符的template版本并不取代默认赋值运算符。

 4. 把class template作为template parameter使用的技巧成为Template Template Parameter。

 5. Template Template Arguments必须完全匹配其对应参数。

 6. 当具现化一个内建类型的变量时,如果需要设定初值,必须明确调用其默认构造函数。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值