类型萃取
类型萃取指的是当泛型编程时,根据一个模板类(我这里就用类来说了,当然模板函数也可以)的模板类型,使用一个traits来获取其类型,获取其类型,通过类型调用不同的函数,比如拷贝函数 ,如果普通类型int、double之类的则调用较快的内存拷贝(memcpy),而需要申请空间的如string、vector等,则使用for循环赋值拷贝。
C++中为了提高代码执行的效率,往往即使需要增加代码的复杂程度,也在所不惜,《STL源码剖析》中称之为无所用其极,其中最典型的例子之一就是类型萃取。
为什么用类型萃取而不用偏特化
偏特化指的是对模板类(或模板函数)的针对某些类型特殊化,通过偏特化可以实现不同类型调用不同函数,同样可以达到int型拷贝时用memmove(或memcpy),用string等作为类型时拷贝用for循环赋值拷贝。
但是这里有个问题,类型那么多,我们每个都需要偏特化,如果一个函数代码是10行,偏特化这个函数,假如有10个类需要偏特化,那么我们就会有100行代码,而且不光有拷贝函数需要偏特化,很多功能函数都需要偏特化,如果有10个函数需要偏特化,那么1000行代码,量还是很大的。
类型萃取就不一样了,我们只需要萃取出类型,就能很轻松地调用对应函数。还是以拷贝函数为例,比如我们把所有类型分为两类,一类是__true_type
则使用memcpy
拷贝,一类是__false_type
使用for
循环赋值拷贝,对string类等一些类型则是不做特化处理,我们自己创建的类型也属于这一类型,进来则被判断为__false_type
,对于另一类int、double、char一类的则做偏特化处理,处理成__true_type
,这样我们的拷贝函数则只要分两种情况就能处理了。
类型萃取也用到了偏特化,但他对类型偏特化处理的代码(1个类型3行,10个类型30行)远远少于直接对函数偏特化(100行),减少了大量的冗余代码,而且也实现了不同类型不同处理方法,效率上有了明显提升,冗余代码量也减少,这种编程技巧,相比于直接偏特化好处太多了,何乐而不为?
代码
struct __true_type
{
static bool get()
{
return true;
}
};
struct __false_type
{
static bool get()
{
return false;
}
};
template <class T>
struct __my__type_traits
{
typedef __false_type is_POD_type;
};
//对int进行偏特化萃取
template <>
struct __my__type_traits<int>
{
typedef __true_type is_POD_type;
};
//对double进行偏特化萃取
template <>
struct __my__type_traits<double>
{
typedef __true_type is_POD_type;
};
//先调类型萃取然后进行判断
template<class T>
T* __my_copy(T* dst, const T* src, size_t size)
{
if (dst == src || dst == nullptr || src == nullptr)
return dst;
if (__my_type_traits<T>::is_POD_type::get() == true)
{
cout << typeid(T).name() << "copy by memcpy" << endl;
memcpy(dst, src, sizeof(T) * size);
}
else
{
cout << typeid(T).name() << "copy by for" << endl;
for (int i = 0; i < size; i++)
{
*(dst + i) = *(src + i);
}
}
return dst;
}
template<class T>
T* __my_move(T* dst, const T* src, size_t size)
{
if (dst == src || dst == nullptr || src == nullptr)
return dst;
if (__my_type_traits<T>::is_POD_type::get() == true)
{
memmove(dst, src, sizeof(T) * size);
}
else
{
if (src < dst && src + size > dst)
{
for (int i = 0; i < size; i++)
{
*(dst + size - i - 1) = *(src + size - i - 1);
}
}
else
{
for (int i = 0; i < size; i++)
{
*(dst + i) = *(src + i);
}
}
}
return dst;
}
测试代码
测试一下上面类型萃取的拷贝管不管用
void test()
{
string src_string[5] = { "1111", "2222", "3333", "4444", "5555" };
string dst_string[5];
cout << "test string copy" << endl;
__my_copy(dst_string, src_string, 5);
for (int i = 0; i < 5; i++)
cout << dst_string[i] << endl;
cout << endl;
int src_int[5] = { 1,2,3,4,5 };
int dst_int[5] = { 0 };
cout << "test int copy" << endl;
__my_copy(dst_int, src_int, 5);
for (int i = 0; i < 5; i++)
cout << dst_int[i] << endl;
}