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

时间:2013-08-01 08:00:38

标签: c++ templates stl

我想知道为什么在STL中的许多模板算法中,参数不是通过引用传递的,而是通过值传递的。以下是<iterator&gt;的示例。头:

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版本中,迭代器是按值传递的原因是什么?

谢谢!

4 个答案:

答案 0 :(得分:10)

我想到的一件事是反对引用中的const:在使用它们时需要修改迭代器。

另一个实现细节可能是迭代器实际上只是作为指针实现的。大多数情况下参考也是如此。如果按值传递指针,则将其复制一次,但仅在需要时取消引用它。但是,如果迭代器指针本身由引用指针传递,则即 strong>必须首先解除引用,只是为了到达迭代器,每次访问迭代器时都必须这样做。这是多余的。

答案 1 :(得分:7)

对于某些廉价复制类型,按值传递它们实际上比通过引用传递更快。来自教程等的传统智慧表明,通过引用更快,但这不一定适用于廉价复制类型。

如何按值传递对象?您复制它,这意味着您获取值并将其推送到堆栈以进行函数调用。你如何通过参考传递?您将内存地址推送到堆栈,然后被调用的函数必须获取该地址的任何内容。现在,优化和缓存可能会发挥作用,使内存提取更便宜,但你仍然没有比从堆栈中获取精确值更便宜。在迭代器的情况下,通常只需要一个简单的指针。这是一个单词长,复制非常便宜。

另外,请注意您建议传递const引用。这意味着它必须在被调用函数中被复制以允许它被修改(例如在循环中递增)。

答案 2 :(得分:5)

std迭代器的概念是generalisation of a pointerstd容器的迭代器通常实现为组成单个指针的类型。如果参数类型的复制价格与指针一样便宜,那么通过引用传递参数比使用值传递它更加更多。必须先取消引用对象,然后才能使用对象的值。有关详细信息,请参阅this answer

因为几乎所有std算法都需要复制迭代器,为了获得最佳性能,迭代器的复制成本非常低。出于这个原因,找到一个迭代器是非常不寻常的,它通过值传递比引用要贵得多。

std::distance - 以及许多其他算法的情况下 - 整个算法非常简单,编译器很可能会内联调用。如果内联调用,则参数是通过引用还是通过值传递并不重要。 [请注意,内联函数调用与内联函数声明不同!]

如果迭代器通过值传递比通过引用更昂贵,并且函数调用没有内联,则可以通过rvalue-reference创建用于传递迭代器的参数。在如此罕见的情况下,性能提升可能不值得额外的复杂性。

答案 3 :(得分:2)

大多数算法会修改其参数。例如,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 当然,实际的实现将针对随机访问迭代器进行优化,以使其具有常量而非线性运行时。以上实施仅供参考。