Повторне використання переміщеного контейнера?


84

Який правильний спосіб повторного використання переміщеного контейнера?

std::vector<int> container;
container.push_back(1);
auto container2 = std::move(container);

// ver1: Do nothing
//container2.clear(); // ver2: "Reset"
container = std::vector<int>() // ver3: Reinitialize

container.push_back(2);
assert(container.size() == 1 && container.front() == 2);

З того, що я прочитав у стандартному проекті C ++ 0x; ver3 здається правильним шляхом, оскільки об'єкт після переміщення знаходиться в

"Якщо не вказано інше, такі переміщені об'єкти повинні бути переведені в дійсний, але невстановлений стан."

Я ніколи не знайшов жодного примірника, де це "вказано інакше".

Хоча я вважаю, що ver3 трохи обхідний, і я б віддав перевагу ver1, хоча vec3 може дозволити додаткову оптимізацію, але з іншого боку може легко призвести до помилок.

Чи моє припущення правильне?


4
Ви можете просто зателефонувати clear, оскільки він не має передумов (і, отже, не покладається на стан об’єкта).
Nicol Bolas

@Nicol: Скажімо, була std::vectorреалізація, яка зберігала вказівник на її розмір (здається безглуздо, але законно). Переміщення з цього вектора може залишити покажчик NULL, після чого clearне вдасться. operator=також може зазнати невдачі.
Ben Voigt

9
@Ben: Я думаю, що це порушить "дійсну" частину "дійсного, але невизначеного".
ildjarn

1
@ildjarn: Я думав, це просто означає, що безпечно запустити деструктор.
Ben Voigt

Я думаю, питання в тому, що є "дійсним"?
ronag

Відповіді:


97

З розділу 17.3.26 специфікації "дійсний, але неуточнений стан":

стан об'єкта, який не вказаний, за винятком того, що інваріанти об'єкта задовольняються, і операції над об'єктом ведуть себе, як зазначено для його типу [Приклад: Якщо об'єкт xтипу std::vector<int>знаходиться в допустимому, але невизначеному стані, x.empty()його можна викликати безумовно і x.front()можна викликати лише якщо x.empty()повертає false. —Кінцевий приклад]

Тому об'єкт живе. Ви можете виконати будь-яку операцію, яка не вимагає попередньої умови (якщо спочатку не перевірити попередню умову).

clear, наприклад, не має передумов. І це поверне об’єкт у відомий стан. Тож просто очистіть це і використовуйте як звичайно.


Де в стандарті я можу прочитати про "передумови", наприклад, для методів std :: vector?
ronag

1
@ronag: §23.2 містить таблиці, де вони перелічені.
Грізлі

2
Я знайшов наступне, що цікаво, open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3241.html , вони пишуть "контейнери можуть бути" порожнішими, ніж порожніми "".
ronag

4
@ronag: 1) Якщо контейнер перебуває у допустимому стані, тоді виклик clearє дійсним. 2) Поки контейнер знаходився у невстановленому стані, виклик clearпереводить контейнер у вказаний стан, оскільки він передбачає встановлені післяумови в стандарті (§23.2.3 таблиця 100). std::vector<T>має інваріант класу, який push_back()завжди справедливий (доки Tє CopyInsertable).
ildjarn

3
@ronag: open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3241.html цитував один із коментарів національного органу щодо цитати "пустіше, ніж порожнє". Коментар національного органу був неправильним. N3241 не пропонував такого стану. Якщо реалізація контейнера std :: має стан "порожній, ніж порожній", що є результатом переміщення, тоді цей стан повинен бути дійсним (тобто з цим об'єктом можна робити все, що не вимагає попередніх умов).
Говард Хіннант

11

Об'єкт, що знаходиться у допустимому, але невизначеному стані, в основному означає, що хоча точний стан об'єкта не гарантується, він є дійсним, і оскільки такі функції-члени (або функції, що не входять до складу) гарантовано працюють, поки вони не покладаються на об'єкті, що має певний стан.

Функція- clear()член не має передумов щодо стану об’єкта (крім того, що вона дійсна, звичайно) і тому може бути викликана для переміщених об’єктів. З іншого боку, наприклад, front()залежить від того, що контейнер не є порожнім, і тому його не можна викликати, оскільки він не гарантовано не буде порожнім.

Тому як ver2, так і ver3 повинні бути добре.


Вектор завжди буде порожнім, але це не відповідає загальному випадку (масив IE)
Mooing Duck

"Вектор завжди буде порожнім", на чому ви це базуєте?
ronag

1
@ronag: Звичайно, я мав на увазі ver2 і ver3 (як повинно бути зрозуміло з тексту, виправлено цю помилку
Grizzly

Цікаво, що передумови для front()викладені лише для std::array, і навіть там немає в таблиці.
Ben Voigt

1
@Ben: §23.2.3 таблиці 100 говорить, що операційна семантика front()are *a.begin(), §23.2.1 / 6 говорить « Якщо контейнер порожній, тоbegin() == end() », а §24.2.1 / 5 говорить « Бібліотека ніколи не припускає, що минуле кінцеві значення неможливо орієнтувати. ". Отже, я думаю, що передумови для цього front()можна зробити, хоча це, безумовно, можна зробити більш чіткими.
ildjarn

-8

Я не думаю, що ви можете зробити що-небудь із переміщеним об’єктом (крім знищення).

Ви не можете swapзамість цього скористатися всіма перевагами пересування, але залишити контейнер у відомому стані?


+1. підкачка - це гарна ідея, хоча вона працюватиме не у всіх випадках, наприклад, використання авто не буде працювати. Може, ідеєю може бути safe_move, який використовує своп всередині?
ronag

5
Це живий об’єкт, і ви можете використовувати будь-які функції, які не мають передумов (окрім інваріантів)
Mooing Duck

Первинний шаблон для std::swapмає 2 призначення переміщення, причому цілі цих призначень переміщуються із значень. Це розраховується як "робити щось із переміщеним об'єктом" для мене
Калет,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.