STL源码剖析之算法:copy & copy_backward

    copy() 是一个调用频率非常高的函数,所以SGI STL的copy算法用尽各种办法,包括函数重载(function overloading)、型别特性(type traits)、偏特化(partial specialization) 编程技巧,无所不用其极地加强效率。下图是整个copy()操作的脉络。

    copy算法将输入区间[first, last)内的元素复制到result指向的输出区间内,赋值操作是向前推进的。如果输入区间和输出区间重叠,复制顺序需要多加讨论。当result位于[first, last)之内时,也就是说,如果输出区间的首部与输入区间重叠,copy的结果可能不正确,建议选用copy_backward;如果输出区间的尾部如输入区间重叠,copy_backward的结果可能不正确,建议选用copy。当然,如果两区间完全不重叠,copy和copy_backward都可以选用。

    copy算法根据输出迭代器的特性决定是否调用memmove()来执行任务,memmove()会先将整个输入区间的内容复制下来,然后再复制到输入区间。这种情况下,即使输入区间和输出区间有重叠时,copy的结果也是正确的。这也回答了上文中提调的,为什么result位于[first, last)之内时,copy的结果只是“可能不正确”。

    copy为输出区间内的元素赋予新值,而不是产生新元素,它不能改变输出区间的长度。换句话说,copy不能用来直接将元素插入到空容器中。如果你想要将元素插入序列之中,要么使用序列容器的insert成员函数,要么使用copy算法并搭配insert_iterator。

    下面是copy算法唯三的对外接口,包括一个完全泛化版本和两个重载函数:

  1. template <class InputIterator, class OutputIterator> 
  2. inline OutputIterator
  3. copy(InputIterator first, InputIterator last, OutputIterator result) { 
  4.     return __copy_dispatch<InputIterator, OutputIterator>()(first, last, result); 
  1. inline char* copy(const char* first, const char* last, char* result) { 
  2.     memmove(result, first, last - first); 
  3.     return result + (last - first); 
  1. inline wchar_t* copy(const wchar_t* first, const wchar_t* last, wchar_t* result) { 
  2.     memmove(result, first, last - first); 
  3.     return result + (last - first); 
   

    copy()函数的泛化版本中调用了一个__copy_dispatch()的仿函数(老方法了^_^),此仿函数有一个完全泛化版本和两个偏特化版本:

  1. template <class InputIterator, class OutputIterator>  
  2. struct __copy_dispatch {  
  3.     OutputIterator operator(){InputIterator first, InputIterator last,  
  4. OutputIterator result) {  
  5.         return __copy(first, last, result, iterator_category(first);  
  6. } ; 
  7.  
  8. /*  
  9.  *  __copy_dispatch()的完全泛化版本根据迭代器种类的不同,调用 
  10.  *  不同的__copy(),为的是不同的迭代器使用的循环条件不同,有
  11.  * 慢之别 
  12.  */ 
  13.  
  14. template <class InputIterator, class OutputIterator> 
  15. inline OutputIterator __copy(InputIterator first, InputIterator last, 
  16. OutputIterator result, input_iterator_tag) { 
  17.     for( ; first != last; ++first, ++result) 
  18.         *result = *first; 
  19.     return result; 
  20. template <class RandomAccessIterator, class OutputIterator> 
  21. inline OutputIterator __copy(RandomAccessIterator first, RandomAccessIterator last, 
  22. OutputIterator result, random_access_iterator_tag) { 
  23.     __return __copy_d(first, last, result, distance_typ(first)); 
  24. template <class RandomAccessIterator, class OutputIterator, class Distance> 
  25. inline OutputIterator __copy_d(RandomAccessIterator first, RandomAccessIterator last, 
  26. OutputIterator result, Distance*) { 
  27.     // 以n决定循环次数,速度快 
  28.     for(Distance n = last - first; n > 0; --n, ++result, ++first) { 
  29.         *result = *first; 
  30.     return result; 
  1. template <class T>  
  2. struct __copy_dispatch<T*, T*> {  
  3.     T* operator()(T* first, T* last, T* result) {  
  4.         typedef typename __type_traits<T>::has_trivial_assignment_operator t;  
  5.         return __copy_t(first, last, result, t());  
  6. };  
  7. template <class T>  
  8. struct __copy_dispatch<const T*, T*> {  
  9.     T* operator()(T* first, T* last, T* result) {  
  10.         typedef typename __type_traits<T>::has_trivial_asssignment_operator t;  
  11.         return __copy_t(first, last, result, t());  
  12. };  
  13. /*
  14. * 这两个偏特化版本是在“参数为原生指针形式”的前提下,利用 
  15.  * __type_traits<>编程技巧来探测指针所指向之物是否有 
  16.  * trivial assignment operator. 如果指针所指对象拥有 
  17.  * trivial assignment operator,则可以通过memmove()进行复制,速度要比 
  18.  * 利用赋值操作赋快许多
  19. */
  20. template <class T> 
  21. inline T* __copy_t(const T* first, const T* last, T* result, __true_type)  { 
  22.     memmove(result, first, sizeof(T) *(last - first); 
  23.     return result + (last - first); 
  24. template <class T> 
  25. inline T* __copy_t(const T* first, const T* last, T* result, __false_type) { 
  26.     return __copy_d(first, last, result, (ptrdiff_t *)0); 

    以上就是copy()的故事,一个无所不用其极强化效率的故事。

    copy_backward()的实现与copy()极为相似,不同是它将[first, last)区间内的每一个元素,以逆行的方向复制到,以result-1为起点,方向同样为逆行的区间上。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章