| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > C/C++: Общие вопросы > r-value refs & move-semantics. что? как? для чего? |
| Автор: boostcoder 9.11.2011, 02:17 | ||||
| итак. что же такое r-value refs & move-semantics? что мне известно об этом?: первое(и вроде как единственное) - позволяет получать ссылки на временные объекты.
такой еще пример:
http://en.cppreference.com/w/cpp/utility/move не может быть чтоб на этом польза от r-value refs & move-semantics закончилась. поделитесь опытом, мыслями. спасибо. |
| Автор: azesmcar 9.11.2011, 09:46 |
| boostcoder Это перемещение объекта, объект может быть не копируемым, но перемещаемым. В некоторых случаях копирование объекта невозможно реализовать, зато запросто можно реализовать перемещение. Например класс std::unique_ptr не поддерживает копирование. Он не считает количество ссылок, т.е. его копирование просто невозможно (или он должен работать как auto_ptr), зато реализовано перемещение этого объекта, что позволяет хранить std::unique_ptr в контейнерах. |
| Автор: boostcoder 9.11.2011, 09:58 |
| azesmcar, но у перемещаемого объекта все равно вызывается деструктор? и если да, то что он разрушает, если данные были перемещены? |
| Автор: azesmcar 9.11.2011, 10:10 | ||
Например:
http://liveworkspace.org/code/4baf51d7d55ab0104835aad73d11a085 Объект перемещается, т.е. все данные переместились в другой объект, в данном случае деструктор сделает delete 0 и успокоится. Что делает деструктор и конструктор перемещения решает программист. |
| Автор: boostcoder 9.11.2011, 10:10 | ||
что-то не понял... вот к примеру:
т.е. async_call() возвращает управление тут же. но если функциональный объект так же, тут же будет разрушен, то когда придет время его вызвать получим ошибку сегментации. или как? Добавлено через 1 минуту и 53 секунды ага. т.е. реализация деструктора должна предполагать подобное поведение? |
| Автор: rumit7 9.11.2011, 10:15 | ||||
Извините что вмешиваюсь, вот по этой http://blogs.msdn.com/b/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx идет вроде не плохое и достаточно подробное объяснение r-value refs & move-semantics. Ну и к одному из приведенному примеру дается такое объяснение: " Instead of dynamically allocating memory, the move constructor and move assignment operator simply steal it from other . When stealing, we copy other's pointer and then null it out. When other is destroyed, its destructor will do nothing.". Там есть примеры показывающие различное поведение r-value refs, а также обсуждается как работает std::move() для различных аргументов:
|
| Автор: boostcoder 9.11.2011, 10:17 | ||||
все равно не понимаю... к примеру, r-value refs у нас нет. тогда мой пример записывался бы так:
т.е. тут создавался бы временный объект, внутри функции async_call() инициализировался бы другой объект, и временный объект бы разрушался. тут все понятно. а вот с r-value что-то не понимаю смысла, если временный объект все равно создается и разрушается.. Добавлено через 1 минуту и 42 секунды rumit7, спасибо. сейчас прочту. |
| Автор: azesmcar 9.11.2011, 10:20 | ||
разрушается не объект а "пустышка", а душа обьекта переселилась в другое тело и живет там в лучших традициях реинкарнации. |
| Автор: boostcoder 9.11.2011, 10:24 | ||
ааа! вот оно как.
вроде понял.. нужно усвоить. |
| Автор: boostcoder 9.11.2011, 12:24 |
| azesmcar, все, понял. спасибо. зы с двухсотым плюсиком тебя. |
| Автор: mes 9.11.2011, 14:43 | ||
хочу обратить внимание, что временный объект не всегда рвалуе и поэтому эти понятия не стоит смешивать..
польза например наблюдается при возвращении массива из функции.. если передачу в функцию еще как то могли смягчить за счет пристыковывания к константной ссылке, то с возвращением временого содержимого руки были связаны.. |
| Автор: baldina 9.11.2011, 14:52 | ||||
оно собственно и задумывалось как средство оптимизации временных объектов, и от этого польза немалая, т.к. для обеспечения производительности раньше в некоторых случаях требовался специальный, менее красивый и простой дизайн.
boostcoder видимо имеет в виду это: http://liveworkspace.org/code/1a70c160da7c185c82dbaf2a272a94a2 хотя имхо польза от этого довольно сомнительная... |
| Автор: boostcoder 9.11.2011, 18:09 |
тоже вариант. |
| Автор: borisbn 9.11.2011, 19:04 |
| Экспериментировал как-то с move-semantics: http://liveworkspace.org/code/ad3e28d4a5542510bcdfe13a7b58d551 Почему-то у меня упорно не хочет вызыватся move-ctor Str( Str && other ); Что я не так делаю... И ещё. Там создаётся 4 объекта (x, y, x и result внутри operator+), а вызывается 3 деструктора. Как это ? И где вызвался конструктор z ? |
| Автор: mes 9.11.2011, 19:24 |
компилятор позволяет себе не переносить из временого в целевой, а сразу использовать целевой объект для результата.. если нужно задействовать именно move -семантику в вашем случае, укажите это с помощью move(); Добавлено через 1 минуту и 20 секунд это по старым правилам |
| Автор: baldina 9.11.2011, 19:32 | ||||
там всего три объекта, погляди на адреса. result и z одно и то же. что бы он вызвался, укажи семантику явно
Добавлено через 4 минуты и 18 секунд это NRVO отработало. велик могучий языка... Добавлено через 6 минут и 1 секунду оказывается уже ответили(( |
| Автор: borisbn 9.11.2011, 19:40 | ||
| mes, baldina, спасибо за ответы, но 1) я хочу, чтобы move-конструктор вызывался автоматически, поэтому не хочу использовать std::move() 2) boostcoder говорит, что на LWS оптимизация выключена, а вы говорите, что
или это не имеет отношения к оптимизации ? тогда вопрос снят |
| Автор: mes 9.11.2011, 19:44 | ||||
не имеет, это не оптимизация, а перенос при инициализации.. некий аналог этому из недалекого прошлого :
так же создается только один объект..
делайте перенос не при инициализации, а при присвоении.. |
| Автор: W4FhLF 5.3.2012, 14:13 | ||||
Это получается как только я вызвал move constructor я уже объектом больше пользоваться не могу потому что данные были перемещены? Разве это не ведёт к понтенциальным ошибкам? Т.е. я должен теперь "запомнить", что объект был перемещён и больше его не трогать. |
| Автор: boostcoder 5.3.2012, 15:55 | ||
угу. move-semantic довольно редко используется, в особых случаях. или же с временными объектами. посему, потенциально не сильно увеличивает вероятность ошибок. |
| Автор: bsa 5.3.2012, 17:54 | ||
объект, который подпадает под rvlaue-reference по любому ты использовать больше не сможешь. В простом случае, это правая часть выражения: a = c + b. Ты по любому после присваивания не сможешь воспользоваться временным объектом. А если у тебя остаются возможности им воспользоваться, то он не является rvalue и, соответственно, не подпадает под действие перемещающего конструктора. |
| Автор: Sahab 13.4.2012, 19:23 | ||||
| Можно ли пользоваться исходным объектом (откуда перемещали) после его перемещения? http://liveworkspace.org/code/3c9358e9ddaef476ffd6df109b6ea1cd
|
| Автор: mes 13.4.2012, 19:55 | ||
нельзя Логика проверки существования значения, путем вывода его в корне не верна.. |
| Автор: boostcoder 13.4.2012, 20:16 |
кстати да. но...раз объекта не существует, то при вызове X::print() разве не должен произойти сегфолт? или разница в том что X::print() это код, а X::data - данные? ведь по логике, код мы перемещать не можем. так? |
| Автор: Sahab 13.4.2012, 20:22 | ||
Я предполагал segfault, именно это я проверял, а не вывод и его значение. Добавлено через 38 секунд Проверял и с данными, сегфолта нет. |
| Автор: mes 13.4.2012, 20:25 | ||
объект (this) существует .. move не трогает сами объекты, а переносит их содержимое кстати в приведенном последнем примере, print демонстриует состояние, а не использует его.. так что по сути все правильно работает |
| Автор: Sahab 13.4.2012, 20:25 |
Я это и предполагал, хотел просто услышать материальное объяснение. |
| Автор: mes 13.4.2012, 20:26 |
потому что ваш move-ctor обнуляет указатель на данные, и следовательно состояние объекта корректно.. |
| Автор: Sahab 13.4.2012, 20:28 |
| http://liveworkspace.org/code/c581e966af26dd6b8b39b963508fd5e4 |
| Автор: mes 13.4.2012, 20:29 |
состояние объекта после переноса неопределено.. |
| Автор: Sahab 13.4.2012, 20:31 |
| Автор: mes 13.4.2012, 20:35 | ||
Вы не то проверяете.. Вы проверяете физическую сущность объекта, а move изменяет состояние.. Добавлено через 1 минуту и 9 секунд Sahab, в Вашем случае коректно.. но такое поведение не гарантированно, а следовательно может быть и подругому .. |
| Автор: boostcoder 13.4.2012, 20:50 |
| что я понял: move перемещает данные объекта, а следовательно - состояние. то, что после перемещения, вызов X::print() не вызывает сегфолт - говорит лишь о том, что код не перемещается. оно и логично. при чтении X::data сегфолт не происходит потому, что X::data после перемещения все равно на что-то указывает. на мусор на стеке, к примеру. |
| Автор: mes 13.4.2012, 20:59 |
код никода не перемещается.. сегфолт происходит не из за какого то перемещения кода.. а из за за доступа к данным за пределами разрешений.. |
| Автор: boostcoder 13.4.2012, 21:07 |
я же так и написал. Добавлено через 4 минуты и 30 секунд ну...почти так |
| Автор: mes 13.4.2012, 21:13 |
| может быть.. я не понял при чем во фразе "код", поэтому и уточнил.. |
| Автор: boostcoder 13.4.2012, 21:33 |
ну как же.. вызов функции это: 1)сохранение стека, 2)call, и т.д.. т.е. если бы перемещение так же бы и переносило и код вызова, то тогда бы точно получали сегфолт. этим я всего лишь хотел сказать то, что успешный вызов X::print() без сегфолта - не показатель того что объект небыл перемещен. собственно, как и чтение переменной в которой до перемещения располагались данные. |
| Автор: mes 13.4.2012, 21:38 |
| Автор: boostcoder 13.4.2012, 22:31 |
возможен. но прежде чем получить сегфолт при доступе к "висячим" данным, нужно произвести вызов X::print(). я же об этом и говорил. а т.к. код не переносится, то и при перенесенном объекте вызов всегда будет успешным. а вот получим ли мы сегфолт при чтении данных - это уже другой вопрос. |
| Автор: bsa 13.4.2012, 23:13 |
| Что вы тут спорите? move-семантика нужна в таких случаях, когда именно перемещение и необходимо. Для простых POD типов она смысла не имеет. А вот для контейнеров самое то. Потому что контейнер содержит несколько указателей. move-конструктор (например) копирует ЗНАЧЕНИЯ этих указателей в новый объект, а в старом (который передается как rvalue) их зануляет. Таким образом, деструктор старого объекта ничего не освободит, и новый объект будет существовать как ни в чем ни бывало. Это просто более быстрый вариант по сравнению с shared_ptr/array при использовании в таких ситуациях. |
| Автор: volatile 13.4.2012, 23:52 | ||
boostcoder, код вообще никогда не переносился, не переносится, и не будет переносится. Он один для всех объектов. Перенесение кода это не С++, более того, я даже не знаю, есть ли вообще в природе язык, где код переносится с экземпляром объекта. |
| Автор: bsa 13.4.2012, 23:58 | ||
это не от языка зависит. А от программиста. Если очень захотеть, то можно это сделать. По крайней мере на платформах, где нет защиты от выполнения кода в области данных. |
| Автор: volatile 14.4.2012, 00:04 |
| bsa, я имел ввиду объекто-ориентированный аспект. само перенесение кода, конечно возможно (хотябы в обычных exe-упаковщиках) Это другое. |
| Автор: boostcoder 14.4.2012, 01:10 | ||
volatile, я не говорил что код переносится. я лишь пытался прояснить это:
|
| Автор: bsa 14.4.2012, 11:16 | ||
boostcoder, исходным объектом пользоваться можно! Более того, исходный объект после перемещения используется как минимум в деструкторе. Но так как ты перенес состояние, то деструктор отработает "вхолостую" без реального освобождения ресурсов. А вот после деструктора объект уже использовать, как ты понимаешь, нельзя. По логике вещей, деструктор вызывается сразу же после отработки операции перемещения:
|
| Автор: mes 14.4.2012, 11:40 |
что значит можно ? да, он существуетн ( и довольно долго если был не временным), но у него неопределено состояние, и соответсвенно, неопределено поведение.. для конкретного случая можно определить возможность использования, можно даже обратно вдохнуть жизнь в такие объекты, но нет возможности распространнить это на все случаи.. поэтому говорить о можно нельзя или иногда можно, но очень осторожно |
| Автор: bsa 14.4.2012, 12:06 |
с чего это вдруг? у него вполне определенное состояние - то, в котором его оставил "перемещатель" (например, если перемещатель занулил только указатель на начало данных вектора, а остальные оставил без изменений, то состояние неопределенное - в общем случае, только деструктор отработает корректно). А это уже задача программиста следить за тем, чтобы состояние соответствовало планам использования. Имхо, после перемещения объект должен быть уничтожен... А "вдыхать" в него жизнь - это только делать себе хуже. |
| Автор: mes 14.4.2012, 12:16 | ||
это то понятно.. но во первых нет гарантий заставляющих оставлять объект в коректном состоянии, во вторых нет (не всегда есть) такой возможности.. а для общего случая нельзя.. |
| Автор: Sahab 14.4.2012, 12:17 |
| йопт... так можно или нельзя? Че в стандарте написано? |
| Автор: mes 14.4.2012, 12:18 | ||
имелось ввиду не состояние конкретного объекта, а состояние любого объекта после перемещения.. Добавлено @ 12:20 вообще применять move семантику нужно к rvalue... но С++, как говорится, всегда готов придти на помощь, и можно применить move для lvalue, однако в этом случае ответственность ложится на плечи программиста, а стандарт умывает руки |
| Автор: bsa 14.4.2012, 12:22 |
| Классно сказано! Ёмко! |