【问题标题】:Does std::copy handle overlapping ranges?std::copy 是否处理重叠范围?
【发布时间】:2010-12-29 12:09:25
【问题描述】:

将数据从一个范围复制到另一个范围时,您必须小心源范围和目标范围之间是否存在部分重叠。如果目标范围的开头与源范围的尾部重叠,则普通的顺序副本将使数据出现乱码。 C 运行时库除了memcpy 之外还有memmove 来处理此类重叠问题。

我假设std::copy 的工作方式类似于memcpy,因为它不考虑源区域和目标区域之间的重叠。如果您尝试使用std::copystd::vector 中的对象“向下”移动,则会损坏数据。是否有类似memmove 的 STL 算法来处理此类情况?还是我应该使用反向迭代器自己动手?

【问题讨论】:

    标签: c++ algorithm stl copy


    【解决方案1】:

    如果输出范围的开始与输入范围重叠,则它不处理重叠范围。

    幸运的是,您可以改用 std::copy_backward(这要求您不要将输出范围的 end 与输入范围重叠)。

    【讨论】:

    • std::copy_backward 在尝试实现和模拟memmove 时很有用。我想减轻调用者的重叠检查负担。
    【解决方案2】:

    std::copy的前提条件,禁止重叠:

    • 原型

      template <class InputIterator, class OutputIterator>
      OutputIterator copy(InputIterator first, InputIterator last,
                          OutputIterator result);
      
    • 前提条件

      • [first, last) 是一个有效范围。
      • result 不是[first, last) 范围内的迭代器。
      • 有足够的空间容纳所有被复制的元素。更多的 正式地,要求是 [result, result + (last - first)) 是一个 有效范围。 [1]

    【讨论】:

    • 这回答了标题中的问题。剩下的问题是是否有 memmove 的类似物,或者我是否必须自己推出。
    • 这仅禁止与目标范围的开始重叠。正如约翰所说,允许与中间或结尾重叠,std::copy_backward 允许与开头(但不是结尾)重叠。
    【解决方案3】:

    C++17 标准草案

    C++17 n4659 standard draft 说:

    28.6.1 “复制”:

    template<class InputIterator, class OutputIterator>
    OutputIterator copy(InputIterator first, InputIterator last,
                        OutputIterator result);
    

    1 要求:结果不得在 [first, last) 范围内。

    2 效果:将范围 [first, last) 中的元素复制到范围 [result, result + (last - first)) 从第一个开始,一直到最后。

    和:

    template<class BidirectionalIterator1, class BidirectionalIterator2>
    BidirectionalIterator2
    copy_backward(
        BidirectionalIterator1 first,
        BidirectionalIterator1 last,
        BidirectionalIterator2 result);
    

    17 要求:结果不得在范围内(第一个,最后一个]。

    18 效果:将范围 [first, last) 中的元素复制到范围 [result - (last-first), 结果)从最后一个 - 1 开始并继续到第一个。 (263) 对于每个正整数 n

    然后注释说明何时使用copy_backward

    263) 当 last 在 [result - (last - first), result) 范围内时,应使用 copy_backward 而不是 copy

    因此,这些功能没有重叠的要求,unlike for memcpy,重叠的行为在Effects 部分中明确定义。

    您只需在它们之间进行选择,因为您通常希望std::copy 用于向左复制,std::copy_backward 用于向右复制。

    C++ 在&lt;algorithm&gt; 中还有一个范围版本的std::move,它可以移动而不是复制。

    【讨论】:

    • 问题的要点是是否有std::memmove 的类似物负责检查调用者的重叠。答案是否定的。
    • @AdrianMcCarthy 感谢您的反馈。我的回答也暗示了这一点,但使用标准报价:-)
    【解决方案4】:

    似乎最直接的方法是创建一个您要复制的范围的临时向量:

    std::vector copiedRange( srcVecIterBegin, srcVecIterEnd);
    std::copy( copiedRange.begin(), copiedRange.end(), srcVecIterCopyLocIter);
    

    您可以将其包装在一个模板函数中,该函数应该能够使用任何容器/迭代器类型进行重叠。

    【讨论】:

    • 是的,但这可能会导致不必要的复制。我宁愿选择一个测试重叠的函数,然后使用正确的复制技术来完成它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-19
    • 2016-06-30
    • 1970-01-01
    相关资源
    最近更新 更多