Ответ 1
Умные указатели предназначены для управления собственностью и продолжительностью жизни, они позволяют нам (среди прочего) безопасно передавать права собственности на различные части нашего кода.
Когда вы передаете const unique_ptr<T>&
функции (не имеет значения, является ли T
const
или нет), то на самом деле это означает, что функция promises никогда не будет изменять сам unique_ptr
(но это все еще может изменить объект с указателем, если T
не const
), т.е. не будет никакой возможной передачи прав собственности. Вы просто используете unique_ptr
как бесполезную оболочку вокруг голого указателя.
Итак, как предложил @MarshallClow в комментарии, вы должны просто избавиться от обертки и передать либо голый указатель, либо прямую ссылку. Что классно с этим, так это то, что ваш код теперь семантически прозрачен (ваша подпись функции четко заявляет, что она не путается с собственностью, которая была не сразу же очевидна с помощью const unique_ptr<...>&
), и он одновременно решает проблему с "констатированием".
А именно:
void someFunction(const AClass* p) { ... }
std::unique_ptr<AClass> ptr(new AClass());
someFunction(ptr.get());
Изменить:, чтобы ответить на ваш вторичный вопрос: "Почему компилятор не позволяет мне... отбрасывать unique_ptr<A>
в unique_ptr<A const>
?".
На самом деле вы можете переместить a unique_ptr<A>
в unique_ptr<A const>
:
std::unique_ptr<A> p(new A());
std::unique_ptr<const A> q(std::move(p));
Но, как вы видите, это означает передачу права собственности с p
на q
.
Проблема с вашим кодом заключается в том, что вы передаете функцию (ссылку на) unique_ptr<const A>
в функцию. Поскольку существует несоответствие типа с unique_ptr<A>
, чтобы заставить его работать, компилятору необходимо создать временную копию. Но если вы не передадите права собственности вручную, используя std::move
, компилятор попытается скопировать ваш unique_ptr
, и он не сможет этого сделать, поскольку unique_ptr
явно запрещает его.
Обратите внимание, как проблема исчезает, если вы перемещаете unique_ptr
:
void test(const std::unique_ptr<const int>& p) { ... }
std::unique_ptr<int> p(new int(3));
test(std::move(p));
Теперь компилятор может создать временный unique_ptr<const A>
и переместить исходный unique_ptr<A>
, не нарушая ваших ожиданий (поскольку теперь ясно, что вы хотите переместить, а не копировать).
Итак, корень проблемы состоит в том, что unique_ptr
имеет только семантику перемещения, а не семантику копирования, но вам понадобится семантика копии для создания временного и все же сохранить право собственности впоследствии. Яйцо и цыпленок, unique_ptr
просто не разработан таким образом.
Если вы теперь рассматриваете shared_ptr
, в котором имеет семантику копирования, проблема также исчезает.
void test(const std::shared_ptr<const int>& p) { ... }
std::shared_ptr<int> p(new int(3));
test(p);
//^^^^^ Works!
Причина в том, что компилятор теперь может создать временную std::shared_ptr<const int>
копию (автоматически кастинг из std::shared_ptr<int>
) и привязать эту временную ссылку к const
.
Я предполагаю, что это более или менее охватывает его, хотя мое объяснение не имеет стандартного жаргона и, возможно, не так ясно, как должно быть.:)