Воображение случай, когда у вас есть unique_ptr
с пользовательским Deleter хранящегося ссылкой:Оператор присваивания unique_ptr копирует удаленный файл, хранящийся в ссылке. Это особенность или ошибка?
struct CountingDeleter
{
void operator()(std::string *p) {
++cntr_;
delete p;
}
unsigned long cntr_ = 0;
};
int main()
{
CountingDeleter d1{}, d2{};
{
std::unique_ptr<std::string, CountingDeleter&>
p1(new std::string{"first"} , d1),
p2(new std::string{"second"}, d2);
p1 = std::move(p2); // does d1 = d2 under cover
}
std::cout << "d1 " << d1.cntr_ << "\n"; // output: d1 1
std::cout << "d2 " << d2.cntr_ << "\n"; // output: d2 0
}
Это было для меня неожиданностью, что назначение в приведенном выше коде имеет побочный эффект копирования d2
в d1
. Я дважды проверить его и обнаружил, что такое поведение, как описано в стандарте в [unique.ptr.single.asgn]:
(1) - Требуется: Если
D
не ссылочный типа,D
должны удовлетворять требованияMoveAssignable
и назначения дебит от rvalue типаD
не должен вызывать исключение. В противном случаеD
является ссылочным типом;remove_reference_t<D>
удовлетворяет требованиямCopyAssignable
, а назначение дебетера из lvalue типаD
не должно вызывать исключения.(2) - Эффекты: собственность Трансферы от
u
до*this
как при вызовеreset(u.release())
с последующимget_deleter() = std::forward<D>(u.get_deleter())
.
Чтобы получить поведение, которое я ожидал (неполную копию справки DeleteR) мне пришлось обернуть ссылку DeleteR в std::reference_wrapper
:
std::unique_ptr<std::string, std::reference_wrapper<CountingDeleter>>
p1(new std::string{"first"} , d1),
p2(new std::string{"second"}, d2);
p1 = std::move(p2); // p1 now stores reference to d2 => no side effects!
Для меня текущей обработки ссылки DeleteR в уникальный PTR является нелогичным и даже к ошибкам:
При сохранении Deleter по ссылке, а не по значению это в основном потому, что вы хотите совместно Deleter с некоторыми важными Uniq ue. Таким образом, вы не ожидаете, что общий делетер будет перезаписан и его состояние будет потеряно после уникального назначения ptr.
Ожидается, что назначение unique_ptr является чрезвычайно чипом, особенно если делетер является ссылкой. Но вместо этого вы получаете копирование делета, что может (неожиданно) дорого.
После присвоения указатель привязан к оригинальной копии отправителя, а не к самому оригинальному делетеру. Это может привести к неожиданным побочным эффектам, если важна личность делета.
Кроме того, текущее поведение предотвращает использование ссылки const для делетера, поскольку вы просто не можете копировать в объект const.
ИМО было бы лучше запретить удаление ссылочных типов и принимать только подвижные типы значений.
Итак, мой вопрос заключается в следующем (это выглядит как два вопроса в одном, извините):
Есть ли причина, почему стандартные
unique_ptr
ведет себя, как это?У кого-нибудь есть хороший пример, где полезно иметь ссылочный тип deleter в
unique_ptr
, а не без ссылки (т. Е. Тип значения)?
Это как ссылки работы - «присвоение» значение для ссылки присваивает значение ссылочного объект; ссылка и объект - одно и то же. Вы не можете заменить ссылку на один объект ссылкой на другой объект, вы можете только заменить значение одного объекта (копией) значения другого объекта. – molbdnilo
Я знаю, как работает ссылочное задание. Я спрашиваю, почему стандарт не сделал unique_ptr более умным в случае хранения ссылки на удаление. Текущее поведение выглядит бесполезным, если не используется std :: reference_wrapper. – oliora
Текущее поведение бесполезно _ для того, что вы пытаетесь сделать, но это не значит, что оно бесполезно. Это означает, что вы не используете его, поскольку он предназначен для использования. –