Почему при возвращении он не может автоматически преобразовать указатель в 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;
}