C++ 标准::复制,标准::向后复制和重叠范围

C++ 标准::复制,标准::向后复制和重叠范围,c++,c++11,stdcopy,C++,C++11,Stdcopy,我指的是和 模板输出副本(输入 首先,最后输入,首先输出d_) 从第一个和第二个开始复制[第一个,最后一个]范围内的所有元素 继续到last-1。如果d_first在 范围[first,last]。在这种情况下,可以使用std::copy_backward 相反 模板BidirIt2向后复制( BidirIt1 first,BidirIt1 last,BidirIt2 d_last) 将元素从[first,last]定义的范围复制到 另一个范围以d_last结尾。元素被反向复制 顺序(首先复制最

我指的是和

模板<类输入,类输出>输出副本(输入 首先,最后输入,首先输出d_)

从第一个和第二个开始复制[第一个,最后一个]范围内的所有元素 继续到last-1。如果d_first在 范围[first,last]。在这种情况下,可以使用std::copy_backward 相反

模板BidirIt2向后复制( BidirIt1 first,BidirIt1 last,BidirIt2 d_last)

将元素从[first,last]定义的范围复制到 另一个范围以d_last结尾。元素被反向复制 顺序(首先复制最后一个元素),但它们的相对顺序是 保存

如果d_last在(first,last].std::copy内,则该行为未定义 在这种情况下,必须使用而不是std::copy_backward

复制重叠范围时,std::copy在复制时是合适的 左侧(目标范围的起点在源之外 范围),而std::copy_backward在复制到 右(目标范围的末尾在源范围之外)

从上述描述中,我得出以下推论:

copy和copy_backward都会复制相同的源范围[第一个,最后一个)到目标范围,尽管在前者的情况下,复制发生在从第一个到最后一个-1之间,而在后者的情况下,复制发生在从最后一个-1到第一个之间。在这两种情况下,源范围中元素的相对顺序保留在最终的目标范围中

但以下两项规定背后的技术原因是什么:

1) 在复制的情况下,如果d_first在范围[first,last]内,则会产生未定义的行为(意味着将源范围复制到目标范围失败,可能是系统故障)

2) 在向后复制的情况下,如果d_last在范围(first,last)内,则会产生未定义的行为(意味着将源范围复制到目标范围失败,可能是系统故障)

我假设,一旦我理解了上述两种说法的含义,用copy_backward替换copy以避免上述未定义的行为场景的建议将变得显而易见

同样,我也假设,一旦我理解了复制和反向复制之间的上述区别,关于向左复制时复制的适当性(这一概念我不清楚)和向右复制时反向复制(这一概念我也不清楚)的提及将开始有意义

一如既往地期待您的帮助

补遗 作为后续工作,我编写了以下测试代码,以验证相同操作的copy和copy_backward行为

#include <array>
#include <algorithm>
#include <cstddef>
#include <iostream>

using std::array;
using std::copy;
using std::copy_backward;
using std::size_t;
using std::cout;
using std::endl;

int main (void)
{
    const size_t sz = 4;

    array<int,sz>a1 = {0,1,2,3};
    array<int,sz>a2 = {0,1,2,3};

    cout << "Array1 before copy" << endl;
    cout << "==================" << endl;

    for(auto&& i : a1) //the type of i is int&
    {
        cout << i << endl;
    }

    copy(a1.begin(),a1.begin()+3,a1.begin()+1);

    cout << "Array1 after copy" << endl;
    cout << "=================" << endl;

    for(auto&& i : a1) //the type of i is int&
    {
        cout << i << endl;
    }

    cout << "Array2 before copy backward" << endl;
    cout << "===========================" << endl;

    for(auto&& i : a2) //the type of i is int&
    {
        cout << i << endl;
    }

    copy_backward(a2.begin(),a2.begin()+3,a2.begin()+1);

    cout << "Array2 after copy backward" << endl;
    cout << "==========================" << endl;

    for(auto&& i : a2) //the type of i is int&
    {
        cout << i << endl;
    }


    return (0);
}
显然,copy会产生预期的结果,而copy_backward不会,即使d_first在范围[first,last]内。此外,d_last也在范围(first,last]内,根据文档,在向后复制的情况下,这将导致未定义的行为

因此,实际上,在向后复制的情况下,程序输出与文档一致,而在复制的情况下则不一致


值得再次注意的是,在这两种情况下,d_first和d_last确实满足了条件,根据文档,这将分别导致copy和copy_backward的未定义行为。然而,只有在copy_backward的情况下才观察到未定义的行为。

这里没有深入的内容。只需运行一个算法即可-使用一种简单的方法完成示例数据:按顺序复制每个元素

假设你有一个四元素数组
inta[4]={0,1,2,3}
,你想把前三个元素复制到后三个元素。理想情况下,你会得到
{0,0,1,2}
。这(不是)如何处理
std::copy(a,a+3,a+1)

步骤1:复制第一个元素
a[1]=a[0];
数组现在是
{0,0,2,3}

步骤2:复制第二个元素
a[2]=a[1];
数组现在是
{0,0,3}

步骤3:复制第三个元素
a[3]=a[2];
数组现在是
{0,0,0}

结果是错误的,因为您在读取某些源数据(
a[1]
a[2]
)之前重写了这些数据。反向复制会起作用,因为按照相反的顺序,您会先读取值,然后再覆盖它们

由于一种合理的方法的结果是错误的,因此标准宣布行为为“未定义”。希望采用幼稚方法的编译器可能会,并且他们不必考虑这种情况。在这种情况下,错误是可以的。采用不同方法的编译器可能会产生不同的结果,甚至可能是“正确的”结果。这也没关系。根据标准,编译器最简单的方法都可以



根据问题的补充:请注意,这并不意味着行为被定义为违背程序员的意图,而是意味着行为不是由C++标准定义的。它由每个编译器决定发生什么。<代码> STD::拷贝(A,A + 3,A+ 1)可以是任何东西。您可能会得到

{0,0,0,0}
的原始结果。但是,您可能会得到
{0,0,1,2}的预期结果
。其他结果也是可能的。你不能仅仅因为幸运地获得了你想要的行为就断定没有未定义的行为。有时,未定义的行为会给出正确的结果。(这就是跟踪与未定义行为相关的错误非常困难的原因之一。)

原因是,一般来说,复制ran的一部分
Array1 before copy
==================
0
1
2
3
Array1 after copy
=================
0
0
1
2
Array2 before copy backward
===========================
0
1
2
3
Array2 after copy backward
==========================
2
1
2
3