Я пытаюсь преобразовать коды для использования интеллектуальных указателей вместо необработанных указателей. Но было интересно - действительно ли умные указатели нельзя использовать в качестве необработанных указателей?
В этом примере:
#include <iostream>
#include <string>
#include <memory>
void printer(int* x)
{
std::cout << "x = " << x << std::endl;
}
int main()
{
auto x = std::make_unique<int>(5);
printer(x);
return 0;
}
Я получаю сообщение об ошибке:
In function 'int main()': 14:14: error: cannot convert
'std::unique_ptr<int, std::default_delete<int> >' to 'int*' for argument '1'
to 'void printer(int*)'
Как это должно быть закодировано? Спасибо!
См. Херб Саттер по этому поводу.
@ πάνταῥεῖ Это не делается неявно по крайне субъективным причинам: «сырые» ptr «небезопасны». Нет никакой внутренней проблемы в том, чтобы разрешить их. И сказать, что слишком опасно разрешать преобразование в ptr, когда вы продолжаете использовать ptr, в корне противоречит.
@MichaelDoubez "если бы неявное преобразование в * было разрешено, компилировалось бы молча -- urk" Сказать, что неявные преобразования позволяют компилировать больше программ, тривиально верно, и это вовсе не убедительный аргумент.
@curiousguy Умные указатели связаны с семантикой владения, и обернутый указатель в этом отношении является гражданином второго сорта. Лучшее, что вы можете ожидать вместо get()
, это неявное преобразование в std::weak_ptr<T>
.
@πάνταῥεῖ Весь смысл использования смарт-указательного указателя заключается в том, чтобы получить доступ к его «гражданину второго сорта», лол.
@curiousguy Вы пропустили "... контролируемым образом" ;)
Неявное преобразование @curiousguy является корнем многих болезненных ошибок. Любая передача права собственности никогда не должна быть неявной, и передача указателя может быть передачей права собственности. Учитывая эту точку зрения и тот факт, что код пишется только один раз, добавленные 6 символов — это небольшие затраты.
@MichaelDoubez 6 символов .get()
также не передают право собственности. По моему опыту, неявные преобразования — это нормально.
@curiousguy цель именно в этом: вы не должны передавать это неявно. Всякий раз, когда мне приходится набирать .get()
, я знаю, что должен быть осторожен. По моему опыту, неявные преобразования редки и используются только тогда, когда это действительно необходимо (например, с утиным типом), а не для синтаксического сахара. Когда даже сделать конструктор с одним параметром явным, чтобы избежать создания экземпляра объекта по ошибке. Без этого анализ воздействия был бы намного сложнее.
@MichaelDoubez "неявные преобразования редки" Вы имеете в виду, что определяемые пользователем неявные преобразования редки, не так ли?
@curiousguy да, я имею в виду пользовательские конверсии. К счастью, компиляторы имеют хорошую поддержку предупреждений об опасных преобразованиях в языке (или поддержку рискованных преобразований, таких как конструктор std::string, принимающий логический параметр).
std::make_unique<>()
возвращает std::unique_ptr
, что делает переменную x
типа std::unique_ptr<int>
. Эти интеллектуальные указатели — это не просто необработанные указатели, и вы не можете использовать их так же, как необработанные указатели. Вместо этого вы должны использовать метод get()
для x
, чтобы получить int*
, на который он указывает.
Это было бы аргументом даже против неявного преобразования в языке.
Неявного преобразования быть не может, потому что неявное преобразование не имеет контекста для выполнения правильных действий. printer
принимает указатель, не являющийся владельцем, но как насчет других функций, которым необходимо стать владельцем? Например
void kill(int *p) { delete p; }
Молча делать конвертацию при звонке kill(p);
было бы неправильно! Это приведет к двойному удалению. У указателя нет способа узнать, что он должен перестать владеть объектом, об этом нужно сообщить через вызов release
. Все остальное было бы неправильно.
Поскольку интеллектуальные указатели предназначены для применения явной семантики владения, преобразование в необработанный указатель также должно быть явным и иметь имя, которое передает, как затрагивается владение.
Очевидно, что передача права собственности должна быть явной (за исключением перемещения копии из rvalue).
Вы должны упомянуть get()
и release()
для стандартных типов указателей.
@curiousguy Это была опечатка, я имел в виду get()
Вам нужно
printer(x.get());
, чтобы получить доступ к необработанному указателю позади. Это не делается автоматически по уважительным причинам.