C++泛型编程

在effective c++中讲到,C++可以分为4个部分:

  1. 兼容C的部分;区块、语句、预处理、内置数据类型、数组、指针统统属于这个部分。但是,没有模板,没有异常,没有重载。
  2. 2.Object-Oriented C++, 这部分指的是C with class, 包括classes(构造函数,析构函数),封装(encapsulation)、继承(inheritance)、多态(polymorphism)、virtual函数(动态绑定)。
  3. Template C++.泛型编程(generic programming),函数模板,模板类,特化,偏特化,特性。
  4. STL,c++标准库,是一个template库,它对容器,迭代器,算法以及函数对象的规约有极佳的紧密配合与协调。

所以泛型编程是学习C++的一大主体,同时也是学习STL的基础。在掌握了C++泛型编程的基本知识以后,再去理解STL会让你事半功倍。

理解模板的原理

首先,我们来看一个简单的小例子,比较两个元素大小,返回较大的值。

//int类型版本
int Max(int a, int b)
{
    return (a>b)?a:b;
}
//long类型版本
long Max(long a, long b)
{
    return (a>b)?a:b;
}
//char类型版本
char Max(char a, char b)
{
    return (a>b)?a:b;
}

以上3个函数的功能都为相同。但是,由于比较的元素不一样,所以我们往往需要编写多个Max比较函数,这样很烦。所以我们使用C++提供的模板机制进行解决。

template <typename T> 
T Max(T a, T b)
{
    return (a>b)?a:b;
}

这时我们还是有疑惑:为什么编写了以上代码以后,我们就可以实现泛型编程了,C++编译器到底是怎么实现这一机制的,同样看个小例子,考虑以下场景:

...
int a = 1;
int b = 2;
int max = Max(a, b);
...

当编译器编译到Max(a, b)时,由于编译器知道a,b是int类型的。所以,编译器就需要一个形参列表为Max(int, int)的版本。如果这时候我们没有声明这个版本。编译器就会使用上述模板代码生成一个int版本。(这个过程也称为实例化)有了这个新生成的int版本,我们的程序就可以通过编译了。
%E6%A8%A1%E6%9D%BF%E7%BC%96%E8%AF%91%E8%BF%87%E7%A8%8B%E5%9B%BE.bmp

通过上述过程,我们可以知道:模板元编程是编写template-based C++程序并执行于编译期的过程。输出为从template具现出来的若干C++源码。再通过函数重载(通过形参列表,不包括返回值)在编译期决定调用的函数版本,这是一种编译器多态,区别于虚函数的运行期多态。

因为多态发生于编译期,所以模板程序往往在编译期需要花费更多的时间,但是运行更快。

函数模板

函数模板是参数化的一族函数(a family of functions)。

Template <typename T>  
Inline T Max(const T& a, const T& b)
{
    return (a>b) ? a : b;
}

上述是一个函数模板的简单例子,这里有两个注意点:

  1. 可以使用class代替typename,但是不能使用struct。
  2. 从语法上使用class和typename没有区别。但是从语义上class可能导致误区,使人以为只有类才能作为型别参数,事实上任何型别都可以。

类模板

与函数模板类似,类也可以通过参数泛化,从而可以构建出一族不同类型的类实例(对象)。类模板实参可以是某一类型或常量(仅限int或enum)。这里需要注意代码膨胀问题,因为如果在程序中填写不同的函数常量,将产生多个版本的代码。

const std::size_t DefaultStackSize = 1024;
template <typename T, std::size_t n = DefaultStackSize > class Stack {
public:
    void Push(const T const & element);
    int Pop(T& element);
    int Top(T& element) const;
private:
    std::vector<T> m_Members;
    std::size_t m_nMaxSize = n;
};

类模板的声明

除了Copy constructor之外,如果在类模板中需要使用到这个类本身,比如定义operator=,那么应该使用完整的定义(Stack),而不是省略型别T。

template <typename T, std::size_t n> class Stack
{
    public:
    ...
    Stack(Stack<T, n> const&);                      //copy constructor               
    Stack<T>& operator= (Stack<T, n> const&);       //assignment operator
    ...
};

类模板的实现

要定义一个类模板的成员函数,则要指明其实一个模板函数的定义应该如下:

template <typename T, std::size_t nMaxSize>
void Stack<T, nMaxSize>::Push(const T const& element)
{
    if(m_Members.size() >= m_nMaxSize){
        // error handing
        return;
    }
    m_Members.push_back(element);
}

typename的细节问题

首先了解两个概念:
1.从属名称:template内出现的名称如果相依于某个template参数。
2.嵌套从属名称:从属名称在class内呈嵌套状。

template<typename C>
void print2nd(const C& container)
{
    if (container.size() >= 2){
    C::const_iterator iter(container.begin());
    ++iter;
    std::cout << value;
    }
}

上述代码中value的类型为int,所以属于非从属名称。但是iter就依赖于template参数,而且在class内呈嵌套状,所以属于嵌套从属名称。但是,这个嵌套从属名称会导致解析困难。如下,如果C::const_iterator为一个C内的静态变量,x为一个全局变量,则下面的代码就变成了算术表达式,与我们所希望的相差甚远。

template <typename C>
void print2nd(const C& container)
{
    //C内的静态变量 全局变量
    C::const_iterator* x;
    ...
}

所以有一个一般性规则为:typename必须作为嵌套从属类型名称的前缀词

typename C::const_iterator iter(container.begin());

但是故事并备有结束,还有一个例外:typename不可以出现在base classes list 内的嵌套从属类型名称之前,也不可在member initialization list(成员初值列)中作为base class修饰符。

template<typename T>
class Derived: public Base<T>::Nested{   //base class list中不允许”typename”
public:
    explicit Derived(int x)
    : Base<T>::Nested(x)                  //mem.init.list中不允许”typename”
    {
        typename Base<T>::Nested temp;   //嵌套从属类型名称             
        ... 
    }
    ...
};

类模板特化

类模板允许对一个类模板的某些模板参数型别做特化。声明如下:

template<>
class Stack<std::wstring> {
    ...
}

但是,特化一个类模板的时候也意味着需要特化其所有参数化的成员函数。以下是特化的作用或好处:

  1. 对于某种特殊的型别,可能可以做些特别的优化或提供不同的实现。
  2. 避免在实例化类的时候引起一些可能产生的诡异行为

特化以后,可以添加新成员函数SetStackSize,也可以采用list作为Stack的内部实现代替vector。

template <> class Stack<std::wstring> {
public:
    void SetStackSize(const std::size_t n) { m_nMaxSize = n; }
    void Push(const std::wstring const & element);
    int Pop(std::wstring& element);
    int Top(std::wstring& element) const;
private:
    std::list<std::wstring> m_Members;
    std::size_t m_nMaxSize = n;
};

类模板偏特化

所谓的偏特化是指提供另一份template定义式,而其本身仍为templatized;也就是说,针对template参数更进一步的条件限制所设计出来的一个特化版本。

特性

//计算一个数组的累加?
template <typename T> inline T Sigma(const T const* start, const T const* end)
{
    T total = T();     //suppose T() actually creates a zero value
    while(start != end)
    {
            total += *start++;
    }
    return total;
}


char szNames[] = "abc";
std::size_t nLength = strlen(szNames);
char* p = szNames;
char* q = szNames + nLength;
printf("Sigma(szNames) = %d\n", Sigma(p,q));

考虑上述代码,调用Sigma(szName)的结果是38(0x26)!而并非期盼的值(97+98+99 = 294)。怎么办呢?

template <typename T> class SigmaTraits
{  };
template <> class SigmaTraits<char>{
    public: typedef int ReturnType;
};
template <> class SigmaTraits<short>{
    public: typedef int ReturnType;
};
template <> class SigmaTraits<int>{
    public:typedef long ReturnType;
};
template <> class SigmaTraits<unsigned int>{
    public:typedef unsigned long ReturnType;
};
template <> class SigmaTraits<float>{
    public:typedef double ReturnType;

template <typename T>
inline typename SigmaTraits<T>::ReturnType Sigma(const T const* start, const T const* end)
{
    typedef typename SigmaTraits<T>::ReturnType ReturnType;
    ReturnType s = ReturnType();
    while(start != end)
            s += *start++;
    return s;
}

通过为每一个Sigma函数的参数型别T创建一种关联(association),关联的型别就是用来存储Sigma结果的型别。这种关联可以看作是型别T的一种特性(characteristic of the type T),因此Sigma函数返回值的型别叫做T的trait。

编译模型

标准c++为编译模板代码定义了两种模型:

  1. 包含编译模型。

     // header file utlities.h
     template <class T> int compare(const T&, const T&);
     #include "utilities.cc"  // get the definitions for compare etc
    
     // implemenatation file utlities.cc
     template <class T> int compare(const T &v1, const T &v2)
     {   ...  }
  2. 分别编译模型。

     // class template header goes in shared header file
     template <class Type> class Queue { ... }
     // Queue.cc implementation file declares Queue as exported
     export template <class Type> class Queue;
     #include "Queue.h"
     // Queue member definitions

转载于:https://www.cnblogs.com/zhangtianqi/p/5102969.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值