vector<int> v;
v.push_back(1);
v.push_back(v[0]);
Если второй push_back вызывает перераспределение, ссылка на первое целое число в векторе больше не будет действительной. Так это небезопасно?
vector<int> v;
v.push_back(1);
v.reserve(v.size() + 1);
v.push_back(v[0]);
Это делает его безопасным?
push_back
. Другой автор заметил в нем ошибку , которая не соответствовала описанному вами случаю. Насколько я могу судить, никто другой не утверждал, что это не ошибка. Не говорю, что это неопровержимое доказательство, просто наблюдение.Ответы:
Похоже, http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#526 решает эту проблему (или что-то очень похожее на нее) как потенциальный дефект в стандарте:
Предлагаемое решение заключалось в том, что это не было дефектом:
источник
v.insert(v.begin(), v[2]);
не может вызвать перераспределение. Так как же это ответить на вопрос?Да, это безопасно, и реализации стандартных библиотек прыгают через препятствия, чтобы сделать это так.
Я считаю, что разработчики каким-то образом прослеживают это требование до 23.2 / 11, но я не могу понять, как это сделать, и не могу найти чего-то более конкретного. Лучшее, что я могу найти, - это эта статья:
http://www.drdobbs.com/cpp/copying-container-elements-from-the-c-li/240155771
Проверка реализаций libc ++ и libstdc ++ показывает, что они также безопасны.
источник
vec.insert(vec.end(), vec.begin(), vec.end());
?vector.push_back
ДОЛЖЕН указать иное. «Вызывает перераспределение, если новый размер больше старого». и (atreserve
) «Перераспределение делает недействительными все ссылки, указатели и итераторы, относящиеся к элементам в последовательности».Стандарт гарантирует безопасность даже первого примера. Цитирование C ++ 11
[sequence.reqmts]
Таким образом, даже если это не совсем тривиально, реализация должна гарантировать, что она не сделает ссылку недействительной при выполнении
push_back
.источник
t
, вопрос только в том, до или после создания копии. Ваше последнее предложение определенно неверно.t
соответствует перечисленным условиям, описанное поведение гарантировано. Реализации не разрешается аннулировать предварительное условие, а затем использовать его как предлог для несоблюдения указанного.for_each
не должен аннулировать итераторы. Я не могу найти ссылку дляfor_each
, но я вижу в некоторых алгоритмах текст вроде «op и binary_op не должны аннулировать итераторы или поддиапазоны».Не очевидно, что первый пример безопасен, потому что простейшей реализацией
push_back
было бы сначала перераспределить вектор, если необходимо, а затем скопировать ссылку.Но, по крайней мере, это кажется безопасным с Visual Studio 2010. Его реализация
push_back
выполняет особую обработку случая, когда вы возвращаете элемент в векторе. Код имеет следующую структуру:источник
Это не гарантия стандарта, но, как еще одна точка данных,
v.push_back(v[0])
безопасна для библиотеки LLVM libc ++ .Libc ++ S
std::vector::push_back
вызовы ,__push_back_slow_path
когда ему необходимо перераспределить память:источник
__swap_out_circular_buffer
, и в этом случае эта реализация действительно безопасна.__swap_out_circular_buffer
. (Я добавил несколько комментариев, чтобы отметить это.)Первая версия определенно НЕ безопасна:
из раздела 17.6.5.9
Обратите внимание, что это раздел о гонках данных, который люди обычно думают в связи с потоковой передачей ... но фактическое определение включает отношения «происходит раньше», и я не вижу какой-либо упорядочивающей взаимосвязи между несколькими побочными эффектами
push_back
in play здесь, а именно, признание недействительности ссылки, похоже, не определяется как упорядоченное в отношении создания копии нового хвостового элемента.источник
v[0]
не является итератором, аналогичноpush_back()
не принимает итератор. Итак, с точки зрения языкового юриста, ваш аргумент недействителен. Сожалею. Я знаю, что большинство итераторов являются указателями, и цель признания итератора недействительной почти такая же, как и для ссылок, но та часть стандарта, которую вы цитируете, не имеет отношения к данной ситуации.x.push_back(x[0])
это БЕЗОПАСНО.Это совершенно безопасно.
Во втором примере у вас есть
который не нужен, потому что, если вектор выходит за пределы своего размера, он будет подразумевать
reserve
.За это отвечает Vector, а не вы.
источник
Оба безопасны, поскольку push_back скопирует значение, а не ссылку. Если вы храните указатели, это все еще безопасно для вектора, но просто знайте, что у вас будут два элемента вашего вектора, указывающие на одни и те же данные.
Поэтому реализации push_back должны гарантировать, что вставлена копия
v[0]
. В противоположном примере, если предположить, что реализация будет перераспределяться перед копированием, она не обязательно добавит копиюv[0]
и, как таковая, нарушит спецификации.источник
push_back
однако также изменит размер вектора, и в наивной реализации это сделает ссылку недействительной до того, как произойдет копирование. Так что, если вы не подтвердите это цитатой из стандарта, я буду считать это неправильным.push_back
скопирует значение в вектор; но (насколько я понимаю) это может произойти после перераспределения, и в этот момент ссылка, из которой он пытается скопировать, больше не действительна.push_back
получает свой аргумент по ссылке .Начиная с 23.3.6.5/1:
Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid.
Поскольку мы вставляем в конце, никакие ссылки не будут признаны недействительными, если размер вектора не будет изменен. Итак, если вектор,
capacity() > size()
то он гарантированно работает, в противном случае гарантированно будет неопределенное поведение.источник
references
часть цитаты.push_back
).