Подтвердить что ты не робот

Зачем нужно, чтобы разделители shared_ptr были CopyConstructible?

В С++ 11 std::shared_ptr имеет четыре конструктора, которые могут быть переданы объектам deleter d типа d. Подписи этих конструкторов следующие:

template<class Y, class D> shared_ptr(Y * p, D d);
template<class Y, class D, class A> shared_ptr(Y * p, D d, A a);
template <class D> shared_ptr(nullptr_t p, D d);
template <class D, class A> shared_ptr(nullptr_t p, D d, A a);

Стандарт требует в [util.smartptr.shared.const] типа d для копирования. Почему это необходимо? Если shared_ptr создает копии d, то какой из этих удалений может быть вызван? Разве не удалось бы shared_ptr сохранить только один делетер? Что значит для shared_ptr владеть удалением, если d можно скопировать?

В чем обоснование требования CopyConstructible?

PS: Это требование может затруднить запись дескрипторов для shared_ptr. unique_ptr, по-видимому, имеет гораздо лучшие требования к своему делетиру.

4b9b3361

Ответ 1

Этот вопрос был настолько озадачен, что я отправил по электронной почте Питеру Димову (исполнитель boost::shared_ptr и участвовал в стандартизации std::shared_ptr)

Вот суть того, что он сказал (перепечатано с его разрешения):

Я предполагаю, что Deleter должен был быть CopyConstructible действительно только как   релиз С++ 03, где семантика перемещения не существовала.

Ваша догадка правильная. Когда указано shared_ptr rvalue еще не существовало. В наши дни мы сможем конструктивный конструктивный вариант.

Есть одна тонкость в том, что когда

pi_ = new sp_counted_impl_pd<P, D>(p, d);

throws, d должен быть оставлен нетронутым для очистки d(p) для работы, но я подумайте, что это не будет проблемой (хотя у меня нет попытался сделать внедрение удобным для перемещения).
[...]
Я думаю, что для чтобы определить, чтобы при нажатии new бросков dв исходном состоянии.

Если мы идем дальше и разрешаем d иметь метательный конструктор перемещения, все получается более сложным. Но мы этого не сделаем.:-)

Ответ 2

Разница между идентификаторами в std::shared_ptr и std::unique_ptr заключается в том, что удаляемый shared_ptr тип стирается, а в unique_ptr тип удаления является частью шаблона.

Вот объясняет Стефан Т. Лававей, как стирание стилей приводит к требованию CopyConstructible в std::function.

Что касается причины этого различия в типах указателей, то он был рассмотрен на SO несколько раз, например. здесь.

Цитата о том, что S.T.L. сказал:

Очень удивительно, что "gotcha" я бы сказал, что для std::function требуются объекты функции CopyConstructible, и это довольно необычно в STL.

Обычно STL ленив в том смысле, что ему не нужны вещи впереди: если у меня есть что-то вроде std::list типа T, T не нужно быть менее чем сопоставимым; только если вы вызываете функцию-член list<T>::sort, тогда она действительно должна быть менее чем сопоставимой.

Правило основного языка, которое дает этому полномочия, заключается в том, что определения функций-членов шаблона класса не создаются, пока они на самом деле не нужны, и тела не существуют в каком-то смысле, пока вы на самом деле не назовете это.

Это обычно классно - это означает, что вы платите только за то, что вам нужно, но std::function является особенным из-за стирания типа, потому что, когда вы создаете std::function из некоторого вызываемого объекта F, ему нужно сгенерировать все, что вы может потребоваться от этого объекта F, потому что он собирается стереть его тип. Он требует всех операций, которые он может когда-либо понадобиться, независимо от того, используются ли они.

Итак, если вы построите std::function из некоторого вызываемого объекта F, F абсолютно необходимо во время компиляции быть CopyConstructible. Это верно, хотя F будет перемещен в std::function, поэтому, даже если вы дадите ему значение r, и даже если вы никогда не копируете std::function в любом месте вашей программы, F по-прежнему требуется для CopyConstructible.

Вы получите сообщение об ошибке компилятора - возможно, ужасно, может быть, приятно - в зависимости от того, что вы получаете.

Он просто не может хранить движимые объекты только функции. Это ограничение конструкции, вызванное тем, что std::function возвращается к boost/TR1, перед ссылками r-value, и в некотором смысле он никогда не может быть исправлен с помощью интерфейса std::function, поскольку он стоит.

Исследуются альтернативы, возможно, у нас может быть другая "подвижная функция", поэтому мы, скорее всего, получим какую-то оболочку с тиснением, которая может хранить подвижную функцию только в будущем, но std::function, поскольку она стоит в С++ 17 прямо сейчас не может этого сделать, поэтому просто будьте в курсе.

Ответ 3

Потому что shared_ptr предназначено для копирования, и любой из этих копий может удалять объект, поэтому все они должны иметь доступ к удаленному пользователю. Для хранения только одного делетера потребуется, ну, пересчет самого делета. Если вы действительно хотите, чтобы это произошло, вы могли бы использовать вложенный std::shared_ptr в качестве делетера, но это звучит немного завышенным.