c++:模板的类型萃取

来源:互联网 发布:2k显示器有必要吗 知乎 编辑:程序博客网 时间:2024/05/16 19:36

首先我们通过一个模板类的顺序表,来了解c++在什么情况下需要对不同的类型不同对待。
这里为了说明重点,只写了增容和尾插部分。

template <class T>class SeqList{public:    SeqList()        :_a(NULL)        , _size(0)        , _capacity(0)    {}    void CheckCapacity()    {        if (_size >= _capacity)        {            _capacity = _capacity > 0 ? _capacity * 2 : 3;//当容量为0时增容为3,之后每次增2倍            T* newA = new T[_capacity];            if (_a != NULL)            {                memcpy(newA, _a, _size*sizeof(T));//使用内置类型:memcpy            }            delete[] _a;            _a = newA;        }    }    void PushBack(const T& x)    {        CheckCapacity();        _a[_size++] = x;    }    void Print()    {        for (size_t i = 0; i < _size; i++)        {            cout << _a[i] << " ";        }        cout << endl;    }private:    T* _a;    size_t _size;    size_t _capacity;};void TestSeqList(){    SeqList<int> s1;    s1.PushBack(1);    s1.PushBack(2);    s1.PushBack(3);    s1.PushBack(4);    s1.Print();}int main(){    TestSeqList();    return 0;}

成功实现尾插四个数字:
这里写图片描述
可是如果我们在测试代码里存string类型,会不会依旧测试成功?

void TestSeqList(){    SeqList<string> s2;    s2.PushBack("1");    s2.PushBack("2");    s2.PushBack("3");    s2.PushBack("4");    s1.Print();}

这种情况运行不能通过。
为什么呢?
这里写图片描述
因为这里用的是memcpy,所以新开辟的空间的前三个_str和_a指向的空间的每一个_str指向的一样。
这样就导致delete[] _a的时候,新开辟的空间的前三个_str为野指针,从而运行不能通过。

其实可以通过全特化来解决这个问题,但因为这样做会让代码的复用性很差,全是重复代码,所以不提倡使用。
还有一种方法是利用operator=,通过for循环依次赋值,这样新旧_str指向的空间不同,解决问题。

 for (size_t i = 0; i < _size; i++) {   newA[i] = _a[i];}

这里提到了全特化
来通过一个例子了解一下:

template<typename T1,typename T2>//全特化class Data{public:    Data()    {        cout<<"Data(T1,T2)"<<endl;    }private:    T1 _d1;    T2 _d2;};template<>class Data<int,char>{public:    Data()    {        cout<<"Data(int, char)"<<endl;    }private:    int _d1;    char _d2;};//template<class T1>  //偏特化//class Data<T1,double>//{//public://  Data()//  {//      cout<<"Data(T1, double)"<<endl;//  }//private://  T1 _d1;//  double _d3;//};int main(){    Data<int,int>d1;    Data<int,char>d2;    return 0;}

这里写图片描述

我们知道一般的类型(比如int,float,double,char等)进行复制的时候采用memcpy效率会高一些;而像string或者是其他的一些自定义类型,继续用mencpy将出现错误,采用for循环进行一个一个复制(赋值运算符重载)才是正确的方法。

但是采用for循环拷贝效率明显不如memcpy函数高,如何才能让程序在运行内置类型时调用memcpy,而拷贝自定义类型时防止错误调用for循环拷贝,两者兼顾呢??

这里就用到了我们今天的主角:
类型萃取
下面是一个简单的类模板

template<class _TP>class Type{};

在一个这个类中加入一个内嵌类型,这样就可以知道是用户自定义类型还是内置类型。

template< >struct TypeTraits<int>{    typedef __TrueType  IsPodType;//内嵌重定义};
template<class _TP>struct TypeTraits{    typedef __FalseType IsPodType;};

在这里,__IsPodType叫做内嵌型别。
PoD:plain old date平凡类型
当遇到自定义类型时,就将__IsPodType定义为__TrueType;
当遇到非自定义类型时,就将__IsPodType定义为__FalseType;
来实现一个例子:

#include<iostream>using namespace std;struct __TrueType{    bool Get()    {        return true;    }};struct __FalseType{    bool Get()    {        return false;    }};template <class T>struct __TypeTraits{    typedef __FalseType IsPodType; //默认为不是基本类型 POD(基本类型)};template <>struct __TypeTraits<int>{    typedef __TrueType IsPodType; //int是基本类型};template <class T>T* TypeCopy(T* dst, const T* src, size_t n){    return __TypeCopy(dst, src, n, __TypeTraits<T>::IsPodType());}template <class T>T* __TypeCopy(T* dst, const T* src, size_t n,__TrueType){    cout << "memcpy()" << endl;    return (T*)memcpy(dst, src, n*sizeof(T));}template <class T>T* __TypeCopy(T* dst, const T* src, size_t n, __FalseType){    for (size_t i = 0; i < n; i++)    {        dst[i] = src[i];    }    cout << "for operator=()" << endl;    return dst;}void TestSeqList(){    string s1[3]={"11","22","33"};    string s2[3]={"0","0","0"};    TypeCopy(s1,s2,3);    int a1[3]={1,2,3};    int a2[3]={0,0,0};    TypeCopy(a1,a2,3);}int main(){    TestSeqList();    return 0;}

这里写图片描述
看起来是不是有点懵逼,来个过程解析图理清思路
这里写图片描述

原创粉丝点击