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爲起點,方向同樣爲逆行的區間上。

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