Std::vector:: insert() отменяет итераторы, если у вектора достаточно места (созданного с помощью резервной копии)?

Отвечать Как скопировать вектор? меня немного смутил об аннулировании итератора. В какой-то литературе говорится: "Если вы используете insert, push_back и т.д., Считайте все итераторы недействительными". Это ясно, это может привести к росту вектора, что делает недействительными итераторы. Как насчет специального случая, когда я знаю, что будет достаточно места?

первая попытка:

myvec.reserve(myvec.size()*3);  //does this protect me from iterator invalidation?
vector<string>::iterator it = myvec.end();    
myvec.insert(myvec.end(), myvec.begin(), it);
myvec.insert(myvec.end(), myvec.begin(), it);

После нескольких отличных ответов попробуйте:

auto size = myvec.size();
myvec.reserve(size*3);  //does this protect me from iterator invalidation?  
myvec.insert(myvec.end(), myvec.begin(), myvec.begin()+size);
myvec.insert(myvec.end(), myvec.begin(), myvec.begin()+size);

После более отличных ответов третья попытка:

auto size = myvec.size();
myvec.reserve(size*3);  //does this protect me from iterator invalidation?  
back_insert_iterator< vector<string> > back_it (myvec);
copy (myvec.begin(),myvec.begin()+size,back_it);
copy (myvec.begin(),myvec.begin()+size,back_it);

Эта цитата из "Справочной библиотеки стандартной библиотеки С++" от Josuttis:

Вставка или удаление элементов аннулирует ссылки, указатели и итераторы, которые ссылаются на следующие элемент. Если вставка вызывает перераспределения, он аннулирует все ссылки, итераторы и указатели.

предлагает, чтобы мой код был безопасным и определял поведение. Есть ли проход в стандарте, который гарантирует это?

Ответ 1

Прошедший итератор всегда немного особенный. Я буду осторожен. Этот стандарт говорит об этом (23.3.6.5):

Если перераспределение не происходит, все итераторы и ссылки до точки вставки остаются в силе.

Ключ здесь - "до точки вставки". Поскольку ваш оригинальный it не находится перед точкой вставки (поскольку это точка ввода), я бы не оставил его в силе.

Ответ 2

Хотя верно, что вставки в вектор не будут вызывать перераспределение до тех пор, пока пропускная способность не будет превышена, и не приведет к недействительности итераторов к элементам до точки вставки (что, возможно, относится к end(), так как @KerrekSB), в таблице 100 стандарта С++ 11 (параграф 23.2.3) указано следующее предварительное условие для функции a.insert(p,i,j) для контейнеров последовательностей:

[...] pre: я и j не являются итераторами в a. [...]

В вашем случае это явно, что заставляет меня думать, что программа имеет Undefined Behavior.

Ответ 3

Итераторы не должны быть недействительными средней функцией. Идея, что память может быть перемещена, не задерживается, потому что вы не можете использовать realloc для объектов с нетривиальными конструкторами. Даже если строительство не было проблемой, все равно пришлось бы скопировать исходную последовательность дважды в худшем случае, отрицая любые преимущества в среднем случае.

Точка, это не имеет смысла реализовать ее таким образом; a alloc, copy, free почти наверняка выполняется независимо от того, что говорит стандарт.

Это безопасно, потому что v.begin() и v.end() всегда являются текущими.

v.insert(v.end(), v.begin(), v.end());
v.insert(v.end(), v.begin(), v.end());

Это не так.

vector<foo>::iterator i = v.begin();
vector<foo>::iterator j = v.end();
v.insert(v.end(), i, j);
v.insert(v.end(), i, j);

Однако сама вставка может быть неустойчивой. Попробуйте следующее в GCC. Вставка self дает неверный результат, только если доступно достаточно памяти (не уверен, что это ошибка).

int main()
{
    int position = 1, first = 2, last = 3;
    // enforce error condition.
    assert(position < first);
    int size = 8;
    // sanity check.
    assert(first < last && last <= size);

    std::vector<int> right, wrong;
    // force resize during insertion.
    right.reserve(size);
    // avoid resize during insertion.
    wrong.reserve(size + (last - first));

    for ( int i = 0; i < size; i++ )
     {
       right.push_back(i);
       wrong.push_back(i);
     }

    std::vector<int>::iterator i;
    i = right.begin();
    right.insert(i + position, i + first, i + last);
    i = wrong.begin();
    wrong.insert(i + position, i + first, i + last);

    assert(right == wrong);
    return 0;
}

Примечание: Вышеупомянутое мнение относится к vector конкретно, а не к контейнерам в целом. Кроме того, предположение о том, что приведенное выше поведение может быть ошибкой, не имеет ничего общего со стандартом, а простота реализации надежной самостоятельной вставки для vector.