C++ class templates(2)---《C++ Templates》

来源:互联网 发布:一键转发微信源码编程 编辑:程序博客网 时间:2024/06/03 16:33

摘要:
1)讲具体类型当做template arguements传入才能使用class template。于是该class template便以你所指定的那些类型,由编译器实例化并编译;
2)class template之中,只有被实际调用的成员函数,才能被实例化;
3)针对某些特定类型,可以针对class template进行全特化和偏特化;
4)可以为template parameters定义默认值,即预设模板变量值。
**

实作class template stack

**

#include <vector>#include <stdexcept>template <typename T>class Stacck{private:    std::vector<T> elems;public:    void push(T const&);    void pop();    T top() const;    bool empty() const{        return elems.empty();    }};template <typename T>void Stack<T>::push(T const& elem){    elems.push_back(elem);}template <typename T>void Stack<T>::pop(){    if(elems.empty(){        throw std::out_of_range("Stack<>::pop: empty stack");        }    T elem=elems.back();    elems.pop_back();    return elem;}template <typename T>T Stack<T>::top() const{    if(elems.empty()){        throw std::out_of_range("Stak<>::top empty stack");    }    return elems.back();}

这个class template是以标准库的class template vector<>为基础构建起来的。这里的typename也可以用class进行代替,这两个几乎没有差异,我么也可以为自己声明Stack类声明copy构造函数和assignment运算符,可以这样写:

template <typename T>class Stack{    ...    Stack(Stack<T> const&);    Stack<T>& operator=(Stack<T> const&);    ...};

然而如果只是需要class名称而不是class类型时,只需写Stack即可,构造函数和析构函数的声明属于这种情况。

**

使用class template

**

#include <iostream>#include <string>#include <cstdlib>#include "stack.hpp"int main(){    try{        Stack<int> intStack;        Stack<string> stringStack;        intStack.push(7);        std::cout<<intStack.pop()<<std::endl;        stringStack.push("hello");        std::cout<<stringStack.top()<<std::endl;        stringStack.pop();        stringStack.pop();    }catch(std::exception const& ex){        std::cerr<<"Exception:"<<ex.what()<<std::endl;        return EXIT_FAILURE;    }}

需要注意的是,惟有被调用到的成员函数,才会被实例化。对于class template而言,只有当某个成员函数被调用时候,才回进行实例化。无疑这样做可以节省时间和空间。另一个好处是,你甚至可以实例化一个class template,而具体实现类型并不需要完整支持内部的部分不会被调用到的函数,举例如下:考虑么讴歌class,其某些成员函数使用operator<对内部元素进行排序;只要避免调用这些函数,就可以以一个不支持operator<的类型来实例化这个class template。
运用typedef,你可以更方便地使用class templates:

typedef Stack<int> IntStack;void foo(IntStack const& s){    IntStack isStack[10];    ...}Stack<float*> floatPtrStack;Stack<Stack<int> > intStackStack;//Stack of Stack

注意,你必须在相邻的两个右角括号之间插入一些空白符号,否则就等于使用了operator>>,就会导致语法错误。

**

class Template的特化

**
针对某些特殊的template arguments,对一个class template进行特化,class template特化与fucntion template的重载类似,使你得以针对某些特定类型进行程序代码优化,或者修正某个特定类型在class template实例中的错误行为。如果你对一个class template进行特化,就必须特化所有成员函数。
特化必须以template<>开头声明此一class,后面耕者你希望的特化结果,特化类型将作为template arguments并在class名称之后直接写明:

template <>class Stack<std::string>{    ...};//特化函数而言,每个T出现处都应该更换为特化类型void Stack<std::string>::push(std::string const& elem){    elems.push_back(elem);}

下面给出一个针对std::string类型特化Stack<>的完整示例:

#include <deque>#include <string>#include <stdexcept>#include "stack.hpp"template<>class Stack<std::string>{private:    std::deque<std::string> elems;public:    void push(std::string const&);    void pop();    std::string top() const;    bool empty() const{        return elems.empty();    }};void Stack<std::string>::push(std::string const& elem){    elem.push_back(elem);}void Stack<std::string>::pop(){    if(elems.empty()){        throw std::out_of_range("Stack<std::string>::pop():empty stack");    }    elems.pop_back();}std::string Stack<std::string>::top() const{    if(elems.empty()){        throw std::out_of_range("Stack<std::string>::top(): empty stack");    }    return elems.back();}

该例中,我们在stack类中改用deque来管理替代元素,这样做并没有特别的好处,但它示范了一个特化实作码可以和其primary template有一定程度的差异,哈哈,这是不是类似于function template中函数重载呀!

**

偏特化(Partial Specialization)

**
class template不仅可以被全特化,更重要的是其可以被偏特化,强大吧!这时的我们可以在特定情况下使用特殊实作码,但仍然留给使用者选择template parameter的能力!例如针对如下class template:

template <typename T1,typename T2>class MyClass{    ...}

以下书中形式的偏特化都是合理的:

template <typename T>class MyClass<T,T>{    ...};
template <typename T>class MyClass<T,int>{    ...};
template <typename T1,typename T2>class MyClass<T1*,T2*>{    ...};
MyClass<int,float> mif;//使用Myclass<T1,T2>Myclass<float,float> mff;//使用Myclass<T,T>MyClass<float,int> mfi;//使用MyClass<T,int>MyClass<int*,float*> mp;//使用<T1*,T2*>MyClass<int,int> m;//使用???,匹配了两种MyClass<int*,int*> m;//使用???,匹配了两种

为了解决上面的歧义性,可以针对相同类型的指针,再提供一种偏特化版本:

template <typename T>class MyClass<T*,T*>{    ...};

**

预设模板自变量

**
我们可以针对class template定义其template parameters的默认值,称为default template arguments(预设模板自变量)

#include <vector>#include <stdexcept>template <typename T,typename CONT=std::vector<T> >class Stack{private:    CONT elems;public:    void push(T const&);    void pop();    T top() const;    bool empty() const{        return elems.empty();    }}//因为template现在有两个参数,因此每一个成员函数的定义式中都必须包含这两个参数template <typename T,typename CONT>void Stack<T,CONT>::pop(){    if(elems.empty()){        throw std::out_of_range("Stack<>::pop(): empty stack");    }    elems.pop_back();}template <typename T,typename CONT>T stack<T,CONT>::top() const{    if(elems.empty()){        throw std::out_of_range("Stack<>::top(): empty stack");    }    return elems.back();}template <typename T,typename CONT>void Stack<T,CONT>::push(T const& elem){    elems.push_back(elem);}

调用

#include <iostream>#include <deque>#include <cstdlib>#include "stack.hpp"int main(){    try{        Stack<int> intStack;        Stack<double,std::deque<double> >dblStack;        intStack.push(7);        std::cout<<intStack.top()<<std::endl;        intStack.pop();        dblStack.push(42.42);        std::cout<<dblStack.top()<<std::endl;        dblStack.pop();        dblStack.pop();    }catch(std::exception const& ex){        std::cerr<<"Exception:"<<ex.what()<<std::endl;        return EXIT_FAILURE;    }}

注意上述代码中的intStack和dblStack的声明方式。