簡體   English   中英

通過值與引用傳遞std算法迭代器參數

[英]Passing std algorithm iterator parameters by value vs. by reference

我想知道為什么在STL中的許多模板算法中,參數不是通過引用傳遞的,而是通過值傳遞的。 以下是<iterator >標頭中的示例:

template<class InputIterator>
typename iterator_traits<InputIterator>::difference_type
distance (InputIterator first, InputIterator last);

當我將兩個迭代器傳遞給此函數時,它們將被復制。 我天真的想法是,最好通過const-reference傳遞這些迭代器,以避免復制迭代器對象:

template<class InputIterator>
typename iterator_traits<InputIterator>::difference_type
distance (const InputIterator &first, const InputIterator &last);

可以說迭代器通常是非常小的對象,復制它們並不昂貴。 但即便如此:便宜的復制將比沒有復制更昂貴。

那么在STL版本中,迭代器是按值傳遞的原因是什么?

謝謝!

我想到的一件事是反對引用中的const :使用它們時需要修改迭代器。

另一個實現細節可能是迭代器實際上只是作為指針實現的。 大多數情況下參考也是如此。 如果按值傳遞指針,則將其復制一次,但僅在需要時取消引用。 然而,如果迭代器指針本身是由參考指針傳遞, 那么必須首先解除引用,只是為了得到迭代器,並且必須在每次迭代器訪問的時間內完成。 這是多余的。

對於某些廉價復制類型,按值傳遞它們實際上比通過引用傳遞更快。 來自教程等的傳統智慧指出,通過引用更快,但這不一定適用於廉價復制類型。

你如何按價值傳遞一個對象? 您復制它,這意味着您獲取值並將其推送到堆棧以進行函數調用。 你如何通過參考傳遞? 您將內存地址推送到堆棧,然后被調用的函數必須獲取該地址的任何內容。 現在,優化和緩存可能會發揮作用,使內存提取更便宜,但你仍然沒有比從堆棧中獲取精確值更便宜。 在迭代器的情況下,通常只需要一個簡單的指針。 這是一個字長,復制非常便宜。

另外,請注意您建議傳遞const引用。 這意味着它必須在被調用的函數中被復制以允許它被修改(例如在循環中遞增)。

std迭代器的概念是指針泛化 std容器的迭代器通常實現為組成單個指針的類型。 如果參數類型與指針一樣便宜,則通過引用傳遞參數比通過值傳遞昂貴。 必須先取消引用對象,然后才能使用對象的值。 有關詳細信息,請參閱此答案

因為幾乎所有的std算法都需要復制迭代器,為了獲得最佳性能,迭代器的復制成本非常低。 出於這個原因,找到一個迭代器是非常不尋常的,它通過值傳遞比引用要貴得多。

std::distance - 以及許多其他算法的情況下 - 整個算法非常簡單,編譯器很可能會內聯調用。 如果內聯調用,則參數是通過引用還是通過值傳遞並不重要。 [請注意,內聯函數調用與內聯函數聲明不同!]

如果迭代器通過值傳遞比通過引用更昂貴,並且函數調用沒有內聯,則可以通過rvalue-reference創建用於傳遞迭代器的參數。 在這種罕見情況下的性能提升可能不值得額外的復雜性。

大多數算法會修改其參數。 例如, distance可以實現如下1

template<class InputIterator>
typename iterator_traits<InputIterator>::difference_type
distance (InputIterator first, InputIterator last) {
    typename iterator_traits<InputIterator>::difference_type result{};
    while (first++ != last)
        ++result;
    return result;
}

顯然,如果您first作為const引用傳遞,則不起作用。

如果將它作為非const引用傳遞它也不起作用,因為在大多數調用上下文中,調用者的對象不能被修改(例如,如果將container.begin()的結果傳遞給函數。

當然你仍然可以通過const引用傳遞,在函數內部復制然后修改它。 在那一點上,我們什么都沒得到。

再加上C ++標准建議,迭代器應該是輕量級類型,復制起來應該很便宜,它更簡單,在大多數情況下通過值傳遞它們更有效

但即便如此:便宜的復制將比沒有復制更昂貴。

不對。 您還需要復制引用(在非內聯函數調用的情況下,它將作為指針實現)。 然后你需要取消引用那個也會增加開銷的指針。 與在許多情況下復制指針一樣便宜的直接拷貝相比,並且不會產生任何解除引用的開銷。


1當然,實際的實現將針對隨機訪問迭代器進行優化,以使其具有常量而非線性運行時。 以上實施僅供參考。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM