Информация об изменениях

Сообщение Re[5]: Ссылки и move semantics от 24.07.2015 22:15

Изменено 24.07.2015 22:17 watchmaker

Здравствуйте, Videoman, Вы писали:


W>>Если другой wrapper_ptr не удаляет память в деструкторе, то указатель можно и не менять — всё будет работать. Если wrapper_ref содержит ссылку — то тем более её нет нужды менять — её «удаление» не уронит программу просто из-за того, что разрушение ссылки в деструкторе — суть отсутствие каких-либо действий.


V>А как быть в случае ссылки и movе оператора?

Достаточно ничего не делать.

V>Я же не могу переприсвоить ссылку?

Для случаев же, когда без этого не обойтись, есть std::reference_wrapper — эту ссылку можно менять.

W>>Тут примерно то же самое — объект остался в консинстентном состоянии, но неизвестно какое это состояние. Так, например, то же перемещение из vector<T> может как сделать объект пустым, так и наполнить его какими-нибудь данными. Оба случая встречаются на практике. В обоих случаях поведение является допустимым, а состояние объекта остаётся согласованным, но вот использовать этот объект просто так не стоит — ведь записав в него через push_back какие-то данные не будет гарантии, что в нём не останется какой-то другой мусор.

W>>Вот именно из-за этого и не стоит использовать объекты после move.

V>Т.е. у такого объекта, в случае со ссылкой, должен быть организован некий zomby mode ?

Нет же.
То есть сделать так можно, но обычно не нужно. В C++ есть много мест где можно прострелить себе ногу, и это не самое популярное. Проверки и подстраховки — это хорошо, но для них есть и более приоритетные места.

Вот есть три популярные реализации стандартной библиотеки С++11: libstdc++, libc++ и вариант от visual c++. Ни в одной из них ничем подобным не занимаются. Можешь сам открыть реализацию, например, уже упомянутого std::reference_wrapper и убедится, что для перемещения ссылок там не задано никаких дополнительных действий, а ссылка-источник никак при этом не изменяется. И все нормально с этим живут. И даже по стандарту.

А тут ситуация больше похожа на то, что сначала выдумал проблему с перемещением ссылок, а теперь пытаешься её героически решить. Хотя самой проблемы нет — просто не трогай ссылки.
Я понимаю, что ты хочешь обезопасится от пользователя, который внезапно захочет работать с объектом из которого сделали move. Но у такого пользователя будут проблемы не только с твоей библиотекой. И проще считать, что не твоя библиотека должна учить его новым основам C++11



V>>>У классов без ссылок я могу переинициализировать члены — я так и делаю.

W>>Ясно, а зачем? Использовал обёртку и выкинул, вместо этого создал другую. Такой вариант тебе не подходит? Ну тогда используй просто указатели вместо ссылок — и вопрос решён.

V>Подходит, но не думал что с ссылкой придется возится с тем, чтобы обеспечить контроль на случай, если перенесенный объект кто-то дернет случайно.


V>Сложность еще в том что до этого класс не имел конструктора по умолчанию и не было логики которая проверяла валидный объект или нет (кроме assert-ов).

V>Теперь придется везде добавлять проверки. Я правильно понимаю?
Дешевый вариант — переехать на указатели и занулять их из move-коснтруктора. Тогда разыменование такого указателя хотя и будет UB, но достаточно часто всё-таки будет приводить к segfault, что вроде как желаемое поведение. Хотя иногда вместо segfault будет феерически глючить, ибо UB.
Но, да, если тебе нужно определенное поведение и гарантии что с таким объектом никто уже не будет работать, то придётся делать проверки на валидность. Для этого можно ибо ввести дополнительный булев флаг(подобно всяким optional<T> и maybe<T> контейнерам), либо опять же переходя на указатели и проверяя их каждый раз на nullptr перед разыменованием (можно, например, тот же reference_wrapper взять за основу, дописать в него соответствующие конструкторы из T&& и засунув проверку в .get).
Re[5]: Ссылки и move semantics
Здравствуйте, Videoman, Вы писали:


W>>Если другой wrapper_ptr не удаляет память в деструкторе, то указатель можно и не менять — всё будет работать. Если wrapper_ref содержит ссылку — то тем более её нет нужды менять — её «удаление» не уронит программу просто из-за того, что разрушение ссылки в деструкторе — суть отсутствие каких-либо действий.


V>А как быть в случае ссылки и movе оператора?

Достаточно ничего не делать.

V>Я же не могу переприсвоить ссылку?

Для случаев же, когда без этого не обойтись, есть std::reference_wrapper — эту ссылку можно менять.

W>>Тут примерно то же самое — объект остался в консинстентном состоянии, но неизвестно какое это состояние. Так, например, то же перемещение из vector<T> может как сделать объект пустым, так и наполнить его какими-нибудь данными. Оба случая встречаются на практике. В обоих случаях поведение является допустимым, а состояние объекта остаётся согласованным, но вот использовать этот объект просто так не стоит — ведь записав в него через push_back какие-то данные не будет гарантии, что в нём не останется какой-то другой мусор.

W>>Вот именно из-за этого и не стоит использовать объекты после move.

V>Т.е. у такого объекта, в случае со ссылкой, должен быть организован некий zomby mode ?

Нет же.
То есть сделать так можно, но обычно не нужно. В C++ есть много мест где можно прострелить себе ногу, и это не самое популярное. Проверки и подстраховки — это хорошо, но для них есть и более приоритетные места.

Вот есть три популярные реализации стандартной библиотеки С++11: libstdc++, libc++ и вариант от visual c++. Ни в одной из них ничем подобным не занимаются. Можешь сам открыть реализацию, например, уже упомянутого std::reference_wrapper и убедится, что для перемещения ссылок там не задано никаких дополнительных действий, а ссылка-источник никак при этом не изменяется. И все нормально с этим живут. И даже по стандарту.

А тут ситуация больше похожа на то, что сначала выдумал проблему с перемещением ссылок, а теперь пытаешься её героически решить. Хотя самой проблемы нет — просто не трогай ссылки.
Я понимаю, что ты хочешь обезопасится от пользователя, который внезапно захочет работать с объектом из которого сделали move. Но у такого пользователя будут проблемы не только с твоей библиотекой. И проще считать, что не твоя библиотека должна учить его новым основам C++11



V>>>У классов без ссылок я могу переинициализировать члены — я так и делаю.

W>>Ясно, а зачем? Использовал обёртку и выкинул, вместо этого создал другую. Такой вариант тебе не подходит? Ну тогда используй просто указатели вместо ссылок — и вопрос решён.

V>Подходит, но не думал что с ссылкой придется возится с тем, чтобы обеспечить контроль на случай, если перенесенный объект кто-то дернет случайно.


V>Сложность еще в том что до этого класс не имел конструктора по умолчанию и не было логики которая проверяла валидный объект или нет (кроме assert-ов).

V>Теперь придется везде добавлять проверки. Я правильно понимаю?
Дешевый вариант — переехать на указатели и занулять их из move-коснтруктора. Тогда разыменование такого указателя хотя и будет UB, но достаточно часто всё-таки будет приводить к segfault, что вроде как желаемое поведение. Хотя иногда вместо segfault будет феерически глючить, ибо UB.
Но, да, если тебе нужно определенное поведение и гарантии что с таким объектом никто уже не будет работать, то придётся делать проверки на валидность. Для этого можно либо ввести дополнительный булев флаг (подобно всяким optional<T> и maybe<T> контейнерам), либо, опять же, перейти на указатели и проверть их каждый раз на nullptr перед разыменованием (можно, например, тот же reference_wrapper взять за основу, дописать в него соответствующие конструкторы из r_w<T>&& и засунуть проверку в .get).