std::copy_n 不会更改目标矢量大小

std::copy_n doesn't change destination vector size

本文关键字:目标 copy std      更新时间:2023-10-16

如果我为向量保留一些空间,然后我用std::copy_n()复制其中的一些值,我会正确复制并可访问的值,但向量的大小仍然为零。这是预期的行为吗?我是否应该调整矢量的大小,即使它没有那么高效?

#include <algorithm>
#include <iostream>
#include <vector>
int main()
{
    std::vector<double> src, dest;
    for(double x = 0.0; x < 100.0; ++x)
        src.push_back(x);
    dest.reserve(src.size());
    std::copy_n(src.cbegin(), src.size(), dest.begin());
    std::cout << "src.size() = " << src.size() << std::endl;
    std::cout << "dest.size() = " << dest.size() << std::endl;
    for(size_t i = 0; i < src.size(); ++i)
        std::cout << dest[i] << "  ";
}

测试的编译器:clang,gcc,Visual C++

但是向量的大小仍然为零

std::copy_n不会更改容器的大小,只需复制值并单步执行迭代器;它甚至没有任何关于容器的信息。因此,代码具有未定义的行为,即使它看起来工作正常。

我是否应该调整矢量的大小,即使它没有那么高效?

是的,您可以使用std::vector::resize而不是std::vector::reserve来解决问题。正如您可能已经想到的那样,这意味着所有元素都将首先由resize构造,然后由copy_n分配。

您可以使用 std::back_inserter,它将通过调用容器的 push_back() 成员函数(即直接构造元素(在容器的末尾附加元素,从而增加容器的大小。

dest.reserve(src.size());
std::copy_n(src.cbegin(), src.size(), std::back_inserter(dest));

关于标准库算法,要记住的关键是它们在范围上运行,而不是在容器上运行。容器是创建范围的方法之一,但它们不是唯一的方法。将结果写入范围的算法假定它们正在写入有效位置;它们不会也不能扩展它们要写入的范围。

因此,当您调用std::copy_n时,您必须提供一个足够大的范围来保存结果。这意味着使用 dest.resize(src.size()); 设置范围,而不仅仅是使用 dest.reserve(std.size()); 分配内存。

或者,可以通过使用 std::back_inserter(dest) 而不是 dest.begin() 调用算法来提供一个知道它已附加到容器并需要调整大小的范围。

std::vector 具有大小容量。它保留了一些比必要的空间更多的空间,以使插入速度更快。保留允许您指定该容量,而调整大小会更改矢量的实际大小

在调用 reserve 后,您的dest分配了内存来存储元素,但它不调用构造函数,它实际上是空的,因此您的代码会导致 UB。使用resize实际创建这些元素,然后就可以了。

dest.resize(src.size());
std::copy_n(src.cbegin(), src.size(), dest.begin());
std::cout << "src.size() = " << src.size() << std::endl;
std::cout << "dest.size() = " << dest.size() << std::endl;
for(size_t i = 0; i < src.size(); ++i)
    std::cout << dest[i] << "  ";