Почему при возвращении он не может автоматически преобразовать указатель в unique_ptr?
Позвольте мне задать свой вопрос на примере.
#include <memory>
std::unique_ptr<int> get_it() {
auto p = new int;
return p;
}
int main() {
auto up ( get_it() );
return 0;
}
Не удается выполнить компиляцию со следующей ошибкой:
a.cpp:5:9: error: could not convert ‘p’ from ‘int*’ to ‘std::unique_ptr<int>’
return p;
^
Почему здесь нет автоматического преобразования с необработанного указателя на уникальный? И что я должен делать вместо этого?
Мотивация: я понимаю, что хорошая практика - использовать интеллектуальные указатели для владения, чтобы быть ясными; Я получаю указатель (который у меня есть) где-то, как int*
в этом случае, и я (думаю, я) хочу его в unique_ptr
.
Если вы планируете комментировать или добавлять свой собственный ответ, пожалуйста, обращайтесь в аргументы Герберта Саттера, чтобы это было возможно в предложении N4029.
Ответы
Ответ 1
Ответ в два раза. Все остальные ответы, включая автоответчик OP, касались только одной половины.
Указатель не может быть автоматически преобразован, потому что:
-
unique_ptr
конструктор из указателя объявлен explicit
, поэтому рассматривается компилятором только в явных контекстах. Это делается для предотвращения случайных опасных преобразований, где unique_ptr
может захватить указатель и удалить его без знаний программиста. В целом, не только для unique_ptr
, считается хорошей практикой объявлять все конструкторы с одним аргументом как explicit
для предотвращения случайных преобразований.
Оператор return - рассматривается стандартным неявным контекстом, поэтому явный конструктор неприменим. Продолжалась дискуссия, если это решение верное, отражено в EWG проблема 114, включая ссылки на несколько предложений: две версии предложений, чтобы сделать
return
явным Herb Sutter (N4029, N4074) и два "ответа", утверждая, что этого не делать: N4094 Говарда Хиннанта и Вилле Вутилайнен и N4131 Филиппа Розена. После нескольких обсуждений и опросов проблема была закрыта как NAD - не дефект.
В настоящее время существует несколько способов:
return std::unique_ptr<int>{p};
или
return std::unique_ptr<int>(p);
В С++ 14 вы также можете использовать автоматический вывод типа возвращаемой функции:
auto get_it() {
auto p = new int;
return std::unique_ptr<int>(p);
}
Обновление: добавлена ссылка на вопрос комитета для второго пункта.
Ответ 2
Поскольку неявное построение unique_ptr
от открытого указателя было бы очень подверженным ошибкам.
Просто создайте его явно:
std::unique_ptr<int> get_it() {
auto p = new int;
return std::unique_ptr<int>(p);
}
Ответ 3
Поскольку std::unique_ptr
берет на себя ответственность за указатель, и вы определенно не хотите случайно получить свой необработанный указатель delete
d.
Если это будет возможно, тогда:
void give_me_pointer(std::unique_ptr<int>) { /* whatever */ }
int main() {
int *my_int = new int;
give_me_pointer(my_int);
// my_int is dangling pointer
}
Ответ 4
Поскольку для преобразования или кастинга требуется соответствующий оператор расстановки (по типу источника) или конструктор (по типу цели). В этом случае должен быть следующим конструктором unique_ptr
:
explicit unique_ptr( pointer p );
у которого есть ключевое слово explicit. OP get_it()
пытается неявное преобразование, которое предотвращает explicit
. Вместо этого OP должен явно построить unique_ptr
, как это было предложено @Stas и @Vincent Savard:
std::unique_ptr<int> get_it() {
auto p = new int;
return std::unique_ptr<int>(p);
}
или даже, если мы хотим только сказать unique_ptr
один раз...
auto get_it() {
auto p = new int;
return std::unique_ptr<int>(p);
}
Ответ 5
Потому что это опасно.
Используйте std::make_unique()
из С++ 14:
std::unique_ptr<int> get_it()
{
auto p = std::make_unique<int>();
return p;
}