Гарантированно ли, что указатели на элементы std::vector действительны после перемещения вектора?

Учитывая этот пример:

std::vector<int> v1 = { 1, 2, 3 };
const int* i = &v1[1];
std::vector<int> v2(std::move(v1));
std::cout << *i << std::endl;

Несмотря на то, что во многих реализациях STL это, вероятно, будет работать, гарантируется ли мне стандартом, что при перемещении std::vector не выполняется перераспределение, а внутренний буфер, поддерживающий v2, такой же, как и у v1? Мне не удалось найти эту информацию ни в Интернете, ни в самом стандарте.


person gd1    schedule 17.08.2014    source источник
comment
Если стандарт ничего не говорит об этом, то, я думаю, это зависит от реализации.   -  person Ashalynd    schedule 17.08.2014
comment
Таблица 99 в N3797 говорит, что X(rv), т.е. создание контейнера из rvalue, должно иметь постоянную сложность (в отличие от линейной для создания копии), я думаю, мы можем сделать вывод из этого элементы должны находиться в одних и тех же ячейках памяти после перемещения. Однако из этого не обязательно следует, что итераторы остаются действительными.   -  person M.M    schedule 17.08.2014
comment
Я не думаю, что в этом случае это гарантировано, но если вы вместо этого сделаете std::vector<int> v2; v2.swap(v1);, то это, безусловно, так. (23.2.1/10)   -  person zch    schedule 17.08.2014
comment
Это открытая проблема 2321 LWG. , см. также home.roadrunner.com/~hinnant/iterator.html   -  person dyp    schedule 17.08.2014
comment
Спасибо за комментарии и ответы: я просто хочу уточнить, что я говорю не об итераторах, а о необработанных указателях на внутренний буфер. Я знаю, что в большинстве реализаций они в основном одинаковы, но...   -  person gd1    schedule 17.08.2014
comment
@gd1 Из LWG 2321: ни один конструктор перемещения [...] контейнера (за исключением array) не делает недействительными любые ссылки, указатели или итераторы, ссылающиеся на элементы исходного контейнера.   -  person dyp    schedule 17.08.2014
comment
@dyp: Это было одобрено? (Пожалуйста, напишите ответ, я думаю, что ваша информация заслуживает этого).   -  person gd1    schedule 17.08.2014
comment
Связанный/дубликат: stackoverflow.com/q/11021764   -  person dyp    schedule 17.08.2014


Ответы (2)


Это открытая проблема 2321 LWG [выделено мной]

Перемещение контейнеров должно (обычно) требоваться для сохранения итераторов.

[...]

[by Stephan T. Lavavej]
23.2.1 [container.requirements.general]/10 говорит, что, если не указано иное, «функция swap() не делает недействительными любые ссылки, указатели или итераторы, относящиеся к элементам контейнеров. заменяется. [Примечание: итератор end() не ссылается ни на один элемент, поэтому он может быть признан недействительным. — примечание в конце]». Однако конструкторы перемещения и операторы присваивания перемещения не имеют аналогичных гарантий аннулирования. Для гарантий требуется несколько исключений, поэтому я не верю, что общий язык, такой как /11, "если не указано иное (либо явно, либо путем определения функция с точки зрения других функций), вызов функции-члена контейнера или передача контейнера в качестве аргумента библиотечной функции не должны делать недействительными итераторы или изменять значения объектов в этом контейнере». применимо.

[2014-02-13 Иссакуа]

Общее согласие в отношении намерений, несколько формулировок и дополнительные абзацы, которые необходимо учесть.

STL для предоставления обновленной формулировки. Перейти к открытию.

Предлагаемое решение:

[...]

никакой конструктор перемещения [...] контейнера (кроме array) не делает недействительными любые ссылки, указатели или итераторы, относящиеся к элементам исходного контейнера. [Примечание. Итератор end() не ссылается ни на один элемент, поэтому он может быть признан недействительным. — конец примечания]

Таким образом, это открытый вопрос, с общим согласием по его основному решению (указатель не должен быть недействительным при перемещении). Однако это официально не признано (пока?) дефектом. Насколько я знаю, все основные реализации не делают указатели недействительными при построении перемещения, и, похоже, это обычно (неявно) предоставляемая гарантия.

person dyp    schedule 17.08.2014
comment
Спасибо. В моем реальном сценарии мне легко обойти проблему, используя swap, и я, вероятно, продолжу это решение. Тем не менее, эта информация очень полезна, и приятно знать, что некоторые люди работают над такими проблемами. - person gd1; 17.08.2014

cppreference.com утверждает, что:

... иметь возможность, но не обязательно, перемещать любые ресурсы, удерживаемые аргументом...

Похоже, что std::move — это всего лишь намек библиотеке на то, что оптимизация путем передачи права собственности возможна, но решать библиотеке, делать эту оптимизацию или нет.

Это означает, что вы должны предполагать, что все указатели на элементы становятся недействительными после перемещения.

person Anders Abel    schedule 17.08.2014
comment
Это полностью упускает из виду пункт std::move. Да, std::move эффективно действует как подсказка к другим функциям. Он позволяет использовать конструктор vector(vector&&). move не предъявляет никаких требований к этому конструктору, в отличие от стандарта, и это является тем, о чем вопрос. - person ; 17.08.2014
comment
«Если аргумент идентифицирует объект, владеющий ресурсами, эти перегрузки могут, но не обязательны, перемещать любые ресурсы, удерживаемые аргументом». В нем говорится, что нет необходимости перемещать все ресурсы в движущихся объектах. Мне интересно, если конструктор перемещения не хочет перемещать ресурсы в объектах (и выполнять эту оптимизацию), почему у него вообще есть конструктор перемещения? - person MRB; 17.08.2014
comment
@MohammadRB Возможно, потому, что он был автоматически сгенерирован компилятором, возможно, потому, что класс должен быть ABI-совместимым с реализацией, которая имеет более функциональный конструктор перемещения, возможно, класс даже не имеет конструктор перемещения, и хотя используется std::move, в конечном итоге используется конструктор копирования C(const C&). - person ; 17.08.2014
comment
@MohammadRB Большинство реализаций std::vector не перемещают объекты ресурсов (каждый элемент контейнера); они просто меняют указатель (перемещают право собственности). - person Shoe; 17.08.2014
comment
Это интересно и полезно, спасибо. Я, вероятно, закончу тем, что использую swap, поскольку он, по-видимому, гарантирует достоверность итератора (и, следовательно, я думаю, указателей). - person gd1; 17.08.2014