11

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

4

1 回答 1

12

std::back_inserter创建一个将元素插入容器的插入迭代器。每次取消引用此迭代器时,它都会调用push_back容器将新元素附加到容器中。

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

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


即使您的容器有足够的容量,它的行为仍然是未定义的:规范指出,在插入时,“如果没有发生重新分配,则插入点之前的所有迭代器和引用仍然有效”(C++11 §23.3.6.5 /1)。

调用push_back等同于在末尾插入,因此std::end(vec)您传入的结束迭代器 ( )std::copy在单次调用 后失效push_back。如果输入范围非空,则程序因此表现出未定义的行为。


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

于 2012-07-16T19:55:15.160 回答