繁体   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