【问题标题】:Does std::copy_n work with overlapping ranges?std::copy_n 是否适用于重叠范围?
【发布时间】:2013-12-23 05:03:03
【问题描述】:

我正在查看 N3485 25.3.1 [alg.copy] 的 C++ 标准,它定义了 4 种算法:

  • copy
  • copy_backward
  • copy_if
  • copy_n

copy的描述中,有这个注释25.3.1 [alg.copy]/3:

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

也就是说,copy 在范围重叠时并不总是正确工作(类似于memcpy)。

copy_backwardcopy_if 具有相似的语言禁止重叠范围(分别为 25.3.1 [alg.copy]/14 和 25.3.1 [alg.copy]/8)。

但是copy_n没有这样的禁止,copy_n_backward也没有。这是否意味着copy_n 在范围重叠时会做正确的事情?

(MSVC++ 的 copy_n 实现似乎委托给 std::memmove,所以我知道它在 MSVC++ 2013 上是安全的。但如果标准另有暗示,我不想依赖它)

【问题讨论】:

  • "MSVC++ 的copy_n 实现似乎委托给std::memmove" 请注意,我们只在这种转换正确的情况下这样做;也就是说,迭代器是指向可简单复制的对象的指针。
  • @James:当然。 :) 在这种情况下,这是一个安全的转换。
  • @James:啊,我明白你的意思了。如果输入是例如std::list 迭代器,这对 MSVC++ 的实现是不安全的。听起来像是对我的回答......
  • 哇,这是一个奇怪的边缘情况,我想说这可能是一个错误(他们打算将其设为 UB),但它似乎几乎是故意遗漏的......
  • 原来的SGI implementation确实对copy_n有这样的要求。我不知道为什么这不在标准中。你可以提交一个问题。顺便说一句,copy 仅禁止在输入范围内输出 开始 的重叠,但当输出范围 结束 在输入范围内(即复制到左侧)时工作正常)。

标签: c++ algorithm stl stl-algorithm


【解决方案1】:

这是安全的*。为什么?因为标准并没有说它安全。他们从 25.3.1 复制的功能有 Requires: 用于他们需要的东西(这是在其他复制形式中声明重叠禁止的地方)。

但是,copy_n 确实没有说它要求范围不重叠,这意味着没关系,因为它没有明确禁止。如果它需要它,它会说明它。

*编辑: 当我的意思是“安全”时,我的意思是这不是未定义的行为或不正确的程序。但是,如果内存范围重叠,则不能保证结果是您可能想要的结果。我们唯一能保证的是:

  1. 对于每个非负整数i < n,执行*(result + i) = *(first + i)
  2. 不禁止调用具有重叠范围的函数,并且不会导致未定义的行为。

因此,我们可以推断,如果范围重叠,则不再保证存储在目标中的结果是源的精确(按顺序)副本。我们保证目标中的每个值都来自源,但确切的值取决于重叠和复制元素的确切顺序。

因此,如果“安全”是指目标始终具有源的完美副本(例如memmove),那么不,它不是“安全”的。但从某种意义上说,它本身不会导致未定义/无效的行为是安全的。

回顾一下,我们保证*(result + i) = *(first + i) 将针对整个范围内的每个元素执行。我们保证,如果范围重叠,程序仍然不是未定义的。我们不保证复制元素的顺序。我们不能保证如果范围重叠,结果中存储的确切值将是什么(但我们知道它们都来自源)。

【讨论】:

  • 当迭代器不支持随机访问时,copy_n 如何进行安全检查? (它只需要一个输入迭代器和一个输出迭代器......)
  • @Billy cplusplus.com 说If the ranges overlap, some of the elements in the range pointed by result may have undefined but valid values. 但我不知道这有多可靠。
  • @remyabel: cplusplus.com == 不可靠。 :)
  • @BillyONEal:已更新。抱歉,我最初所说的“安全”与您可能使用的不同。
  • 所以允许通过重叠范围,但实际上这样做会产生不可预测的结果,因此在所有可能的情况下都没有用?多么奇怪的规格。为什么不把它和其他 copy 函数一样对待呢?他们可能一直在想什么? (我同意规范似乎是这样说的,所以 +1。但多么奇怪。)
【解决方案2】:

我同意 Cornstalks 的回答,并对其 +1。但是,理论必须有实践的支持。

快速浏览 GCC (libstdc++-v3) 和 Clang (libc++) 的实现表明它们的 copy_n 与(或委托给)copy 相同,但在将对象移动到更高位置时不支持重叠地址。

因此,MSVC 赢得了这一轮,至少在委托给 memmove 的 POD 案例中。

【讨论】:

    猜你喜欢
    • 2010-12-29
    • 1970-01-01
    • 2011-06-10
    • 1970-01-01
    • 2020-04-12
    • 2016-06-30
    • 1970-01-01
    • 2023-04-11
    • 2018-07-18
    相关资源
    最近更新 更多