简体   繁体   English

通过值与引用传递std算法迭代器参数

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

I'm wondering why in many template algorithms in the STL the arguments are not passed by reference but rather by value. 我想知道为什么在STL中的许多模板算法中,参数不是通过引用传递的,而是通过值传递的。 Here is an example from the <iterator > header: 以下是<iterator >标头中的示例:

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

When I pass two iterators to this function, they are copied. 当我将两个迭代器传递给此函数时,它们将被复制。 My naive thoughts are that it would be better to pass these iterators by const-reference to avoid copying the iterator objects: 我天真的想法是,最好通过const-reference传递这些迭代器,以避免复制迭代器对象:

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

One could say that iterators are in general very small objects and that copying them is not expensive. 可以说迭代器通常是非常小的对象,复制它们并不昂贵。 But even still: cheap copying would be more expensive than no copying at all. 但即便如此:便宜的复制将比没有复制更昂贵。

So what is the reason that in the STL-version, the iterators are passed by value? 那么在STL版本中,迭代器是按值传递的原因是什么?

Thank you! 谢谢!

One thing that comes to my mind and which is against the const ness in the reference: the iterators need to be modified when using them. 我想到的一件事是反对引用中的const :使用它们时需要修改迭代器。

Another implementation detail may be that iterators are really just implemented as pointers. 另一个实现细节可能是迭代器实际上只是作为指针实现的。 So are references in most cases. 大多数情况下参考也是如此。 If you pass the pointer by value, you copy it once but dereference it only when needed. 如果按值传递指针,则将其复制一次,但仅在需要时取消引用。 If, however, the iterator-pointer itself is passed by a reference-pointer, then that has to be dereferenced first, just in order to get to the iterator, and that must be done each time the iterator is accessed. 然而,如果迭代器指针本身是由参考指针传递, 那么必须首先解除引用,只是为了得到迭代器,并且必须在每次迭代器访问的时间内完成。 That is superfluous. 这是多余的。

For certain cheap-to-copy types, passing them by value is actually faster than passing by reference. 对于某些廉价复制类型,按值传递它们实际上比通过引用传递更快。 Conventional wisdom from tutorials, etc., states that by reference is faster, but this does not necessarily apply to cheap-to-copy types. 来自教程等的传统智慧指出,通过引用更快,但这不一定适用于廉价复制类型。

How do you pass an object by value? 你如何按价值传递一个对象? You make a copy of it, which means you take the value and push it to the stack for the function call. 您复制它,这意味着您获取值并将其推送到堆栈以进行函数调用。 And how do you pass by reference? 你如何通过参考传递? You push the memory address to the stack, and then the called function has to fetch whatever is at that address. 您将内存地址推送到堆栈,然后被调用的函数必须获取该地址的任何内容。 Now, optimization and caching may come into play to make that memory fetch much cheaper, but you still don't get cheaper than taking the exact value from the stack. 现在,优化和缓存可能会发挥作用,使内存提取更便宜,但你仍然没有比从堆栈中获取精确值更便宜。 Which in the case of iterators is often something as simple as a mere pointer. 在迭代器的情况下,通常只需要一个简单的指针。 Which is one word long and so very cheap to copy. 这是一个字长,复制非常便宜。

Also, note you're suggesting to pass a const reference. 另外,请注意您建议传递const引用。 Which means it would have to be copied anyway in the called function to let it be modified (such as incrementing in a loop). 这意味着它必须在被调用的函数中被复制以允许它被修改(例如在循环中递增)。

The concept of std iterators is the generalisation of a pointer . std迭代器的概念是指针泛化 The iterators of the std containers are commonly implemented as a type that composes a single pointer. std容器的迭代器通常实现为组成单个指针的类型。 In the case of an argument type that is as cheap to copy as a pointer, passing the argument by reference is more expensive than passing it by value. 如果参数类型与指针一样便宜,则通过引用传递参数比通过值传递昂贵。 A reference to an object must be dereferenced before the object's value can be used. 必须先取消引用对象,然后才能使用对象的值。 See this answer for more details. 有关详细信息,请参阅此答案

Because almost all std algorithms need to make copies of iterators, in order to obtain the best performance it's already essential that an iterator is cheap to copy. 因为几乎所有的std算法都需要复制迭代器,为了获得最佳性能,迭代器的复制成本非常低。 For this reason, it's very unusual to find an iterator which is significantly more expensive to pass by value than by reference. 出于这个原因,找到一个迭代器是非常不寻常的,它通过值传递比引用要贵得多。

In the case of std::distance - and many other algorithms - the entire algorithm is so simple that the call will very likely be inlined by the compiler. std::distance - 以及许多其他算法的情况下 - 整个算法非常简单,编译器很可能会内联调用。 If the call is inlined, it does not matter whether the argument passed by reference or by value. 如果内联调用,则参数是通过引用还是通过值传递并不重要。 [Note that an inlined function call is not the same as an inline function declaration!] [请注意,内联函数调用与内联函数声明不同!]

In the case of an iterator being more expensive to pass by value than by reference, and the function call not being inlined, you could make an argument for passing iterators by rvalue-reference. 如果迭代器通过值传递比通过引用更昂贵,并且函数调用没有内联,则可以通过rvalue-reference创建用于传递迭代器的参数。 The performance gain in such a rare case is probably not worth the additional complexity. 在这种罕见情况下的性能提升可能不值得额外的复杂性。

Most algorithms modify their arguments. 大多数算法会修改其参数。 For instance, distance might be implemented as follows 1 : 例如, 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;
}

Clearly this doesn't work if you pass first as a const reference. 显然,如果您first作为const引用传递,则不起作用。

And it also doesn't work if you pass it as a non- const reference because in most calling contexts the caller's objects cannot be modified (for instance if you pass the result of container.begin() to the function. 如果将它作为非const引用传递它也不起作用,因为在大多数调用上下文中,调用者的对象不能被修改(例如,如果将container.begin()的结果传递给函数。

Of course you could still pass by const reference, make a copy inside the function and then modify that. 当然你仍然可以通过const引用传递,在函数内部复制然后修改它。 At which point we'd have gained exactly nothing. 在那一点上,我们什么都没得到。

Coupled with the C++ standard recommendation that iterators should be lightweight types that should be cheap to copy, it's more simple and in most cases more efficient to pass them by value: 再加上C ++标准建议,迭代器应该是轻量级类型,复制起来应该很便宜,它更简单,在大多数情况下通过值传递它们更有效

But even still: cheap copying would be more expensive than no copying at all. 但即便如此:便宜的复制将比没有复制更昂贵。

Not true. 不对。 You also need to copy the reference (which, in the case of a non-inlined function call, is going to be implemented as a pointer). 您还需要复制引用(在非内联函数调用的情况下,它将作为指针实现)。 And then you need to dereference that pointer which also adds overhead. 然后你需要取消引用那个也会增加开销的指针。 Compared to a straight copy which may be as cheap as copying a pointer in many cases, and doesn't incur any dereferencing overhead. 与在许多情况下复制指针一样便宜的直接拷贝相比,并且不会产生任何解除引用的开销。


1 Of course a real implementation would be optimised for random access iterators to have constant rather than linear runtime. 1当然,实际的实现将针对随机访问迭代器进行优化,以使其具有常量而非线性运行时。 The above implementation is for exposition only. 以上实施仅供参考。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM