算法 — copy
最后更新于:2022-04-01 19:54:28
为了效率,copy算法可谓无所不用其极,通过分析copy算法能够体会STL的精妙。
首先是三个对外接口:
~~~
template // 泛化版本
inline OutputIterator copy(InputIterator first, InputIterator last,
OutputIterator result)
{
return __copy_dispatch()(first, last, result);
}
inline char* copy(const char* first, const char* last, char* result) { // 针对原生指针的重载
memmove(result, first, last - first);
return result + (last - first);
}
inline wchar_t* copy(const wchar_t* first, const wchar_t* last, // 针对原生指针的重载
wchar_t* result) {
memmove(result, first, sizeof(wchar_t) * (last - first));
return result + (last - first);
}
~~~
如果传入的迭代器是字符型的原生指针,那么直接使用底层的memmove拷贝,效率是非常高的,但如果是普通的迭代器,则需要进一步分析了。再看看__copy_dispatch函数,此函数又兵分三路,包括一个泛化版本和两个偏特化版本:
~~~
template // 泛化版本
struct __copy_dispatch
{
OutputIterator operator()(InputIterator first, InputIterator last,
OutputIterator result) {
return __copy(first, last, result, iterator_category(first));
}
};
template
struct __copy_dispatch // 特化版本
{
T* operator()(T* first, T* last, T* result) {
typedef typename __type_traits::has_trivial_assignment_operator t;
return __copy_t(first, last, result, t());
}
};
template
struct __copy_dispatch // 特化版本
{
T* operator()(const T* first, const T* last, T* result) {
typedef typename __type_traits::has_trivial_assignment_operator t;
return __copy_t(first, last, result, t());
}
};
~~~
首先分析泛化版本。如果迭代器仍为普通迭代器,则调用泛化版本。它要根据迭代器的类型(输入或随机)调用不同的函数:
~~~
template
inline OutputIterator __copy(InputIterator first, InputIterator last,
OutputIterator result, input_iterator_tag) // 输入迭代器
{
for ( ; first != last; ++result, ++first)
*result = *first;
return result;
}
template
inline OutputIterator
__copy(RandomAccessIterator first, RandomAccessIterator last,
OutputIterator result, random_access_iterator_tag) // 随机迭代器
{
return __copy_d(first, last, result, distance_type(first));
}
~~~
由于输入迭代器的移动只能靠operator++,所以采用逐个赋值。如果是随机迭代器,则继续往下调用:
~~~
template
inline OutputIterator
__copy_d(RandomAccessIterator first, RandomAccessIterator last,
OutputIterator result, Distance*)
{
for (Distance n = last - first; n > 0; --n, ++result, ++first)
*result = *first;
return result;
}
~~~
这里之所以要再单独定义一个函数是因为下述的原生指针版本也可能会调用它。由于是随机迭代器,所以它是以n是否大于0为循环判断条件。相比于输入迭代器的判断条件first != last,这个版本显然效率是要高一些的。这就充分利用了迭代器之间的区别尽可能的进行效率优化。
下面分析两个特化版本。当迭代器为原生指针时,调用__copy_t,它的第三个参数是用来判断指针所指类型是否真的需要用复制操作符来一个个复制(也就是判断是trivial还是non-trivial),这种判断工作就交给了类型萃取器__type_traits来完成。
根据是否需要单独复制可以把__copy_t分成两个版本:
~~~
template
inline T* __copy_t(const T* first, const T* last, T* result, __true_type) { // trivial
memmove(result, first, sizeof(T) * (last - first));
return result + (last - first);
}
template
inline T* __copy_t(const T* first, const T* last, T* result, __false_type) { // non-trivial
return __copy_d(first, last, result, (ptrdiff_t*) 0);
}
~~~
trivial版本就很容易了,直接memmove,不需要做过多的复制动作。而non-trivial版本的拷贝则需要逐一进行。由于原生指针属于随机迭代器,所以它可以退而求其次,调用刚才介绍的__copy_d函数。
至此,一个既支持泛化,又具有极高效率的copy函数诞生了!
参考:
《STL源码剖析》 P314.
';