博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

[c++][template]模板基础知识

Posted on 2010-06-11 09:46  xuczhang  阅读(1172)  评论(0编辑  收藏  举报

 

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)显式实例化

手工对需要的模板类型进行实例化,而不依赖编译器。这种做法的好处就是可以把模板函数的声明和定义分开,并且可以精确控制模板实例的准确位置,坏处就是需要人工的跟踪每个需要实例化的实体,不适合用于大项目。

 

包含模型和显式实例化整合

image image

 

(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"