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
.