使用copy和back_inserter将矢量附加到其自身时出现错误结果



受到这个问题的启发,询问如何将向量附加到其自身,我的第一个想法是以下(是的,我意识到insert现在是更好的选择):

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
int main() {
    std::vector<int> vec {1, 2, 3};
    std::copy (std::begin (vec), std::end (vec), std::back_inserter (vec));
    for (const auto &v : vec)
        std::cout << v << ' ';
}

但是,这会打印:

1 2 3 1 * 3

每次运行程序时,*都是不同的数字。事实上,只有2被替换是很奇怪的,如果真的有解释的话,我很想听听。继续说,如果我附加到另一个向量(原始向量的副本),它会正确输出。如果我在copy之前加上以下行,它也会正确输出:

vec.reserve (2 * vec.size());

我觉得std::back_inserter是一种将元素添加到容器末尾的安全方法,尽管事先没有保留内存。如果我的理解是正确的,复制行有什么问题?

我认为这与编译器无关,但我使用的是GCC 4.7.1。

std::back_inserter创建一个插入迭代器,用于将元素插入到容器中。每次这个迭代器被取消引用时,它都会在容器上调用push_back,以向容器添加一个新元素。

对于std::vector容器,调用push_back,其中v.size() == v.capacity()将导致重新分配:创建一个新数组来存储向量的内容,其当前内容复制到新数组中,并销毁旧数组。此时,向量中的任何迭代器都是无效的,这意味着它们不能再使用了。

在您的程序中,这包括begin(vec)end(vec)定义的输入范围,copy算法正从中复制。算法继续使用这些迭代器,即使它们是无效的,因此您的程序显示出未定义的行为。


即使你的容器有足够的容量,它的行为仍然是未定义的:规范规定,在插入时,"如果没有重新分配,插入点之前的所有迭代器和引用都保持有效"(C++11§23.3.6.5/1)

push_back的调用相当于在末尾插入,因此您传递给std::copy的末尾迭代器(std::end(vec))在对push_back进行一次调用后将无效。如果输入范围是非空的,则程序因此表现出未定义的行为。


请注意,如果使用std::deque<int>std::list<int>,程序的行为将是明确定义的,因为当添加元素时,这两个容器都不会使迭代器无效。

相关内容

  • 没有找到相关文章

最新更新