我在 N3485 25.3.1 [alg.copy] 上查找了 C++ 标准,它定义了 4 种算法:
copy
copy_backward
copy_if
copy_n
在copy
的描述中,有这样的注释 25.3.1 [alg.copy]/3:
要求:结果不得在[首、末一]范围内
也就是说,当范围重叠时,copy
并不总是正常工作(类似于memcpy
)。
copy_backward
和copy_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上是安全的。但如果标准另有暗示,我不想依赖这个)
它是安全的*。为什么?因为标准没有说它不安全。他们从 25.3.1 复制函数有Requires:对于他们需要的东西(这是在其他复制表单中说明重叠禁止的地方)。
但是,copy_n
并没有说它要求范围不重叠,这意味着没关系,因为它没有明确禁止。如果它需要它,它会说明它。
*编辑:当我的意思是"安全"时,我的意思是它不是未定义的行为或格式错误的程序。但是,如果内存范围重叠,则不能保证结果是您可能想要的结果。我们唯一可以保证的是:
- 对于每个非负整数
i < n
,执行*(result + i) = *(first + i)
- 不禁止使用重叠范围调用函数,并且不会导致未定义的行为。
因此,我们可以推断,如果范围重叠,则存储在目标中的结果不再保证是源的精确(按顺序)副本。我们保证目标中的每个值都来自源,尽管这些值的确切位置取决于重叠和复制元素的确切顺序。
因此,如果您所说的"安全"是指目的地始终具有源的完美副本(如memmove
),那么不,它不是"安全的"。但它是安全的,因为它本身不会导致未定义/无效的行为。
回顾一下,我们保证对整个系列中的每个元素都*(result + i) = *(first + i)
。我们保证,如果范围重叠,程序仍然不是未定义的。我们不保证元素的复制顺序。我们不能保证如果范围重叠,结果中存储的确切值将是什么(但我们知道它们都来自源)。
我同意Cornstalks的回答,并且+1。但是,理论必须得到实践的支持。
快速浏览一下GCC(libstdc++-v3)和Clang(libc++)的实现,就会发现它们的copy_n
与copy
相同(或委托给),在将对象移动到更高的地址时不支持重叠。
因此,MSVC赢得了这一轮,至少在POD的情况下,该案例委托memmove
。