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

时间:2012-07-16 19:48:49

标签: c++ stl iterator undefined-behavior stl-algorithm

受到this question的启发,询问如何将一个向量附加到自身,我的第一个想法是以下(是的,我意识到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。

1 个答案:

答案 0 :(得分:12)

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的调用等同于最后的插入,因此在调用{{1}后,传递给std::end(vec)的结束迭代器(std::copy)无效}。如果输入范围是非空的,则程序会显示未定义的行为。


请注意,如果使用push_backstd::deque<int>,程序的行为将被明确定义,因为这些容器在附加元素时都不会使迭代器无效。

相关问题