Почему сброс std :: unique_ptr не совпадает с присваиванием?

Я пытаюсь понять, почему

std::unique_ptr<MyClass> p = new MyClass; 

Не работает, но

std::unique_ptr<MyClass> p;
p.reset(new MyClass);

Это хорошо. Я несколько понимаю, как они разные, но я хотел бы знать, почему был сделан выбор, чтобы сделать их разными. Какова опасность при назначении не такая же, как сброс?

Ответ 1

Во-первых, std::unique_ptr<MyClass> p = new MyClass; не является присвоением, это инициализация копирования. И это не работает, потому что конструктор std::unique с необработанным указателем помечен как explicit:

explicit unique_ptr( pointer p ) noexcept;

Он объявлен как explicit чтобы избежать опасных (неожиданных) неявных преобразований, например:

void foo(std::unique_ptr<int> uptr);

int *rptr = new int;
foo(rptr); // suppose rptr is implicitly converted to std::unique_ptr<int>
           // then the ownership is passed to the parameter uptr

// when foo() returns uptr is destroyed; the pointer managed by it is deleted too
// since rptr has been deleted continue to deference on it leads to UB
*rptr = 42; // UB

Обратите внимание, что explicit конструкторы не рассматриваются при инициализации копии (например, std::unique_ptr<MyClass> p = new MyClass;). Вы можете использовать их при прямой инициализации (например, std::unique_ptr<MyClass> p (new MyClass);). Они используются для запрета неявных преобразований, но вы можете выполнять явные преобразования. Подобно использованию reset, вы должны делать это явно, чтобы показать (и заставить себя), что вы полностью уверены в том, что вы делаете.

BTW: назначение из raw-указателя тоже не работает, потому что std::unique_ptr не имеет перегруженного оператора присваивания, беря в качестве параметра необработанный указатель. По вышеуказанной причине необработанный указатель не может быть неявно преобразован в std::unique_ptr, поэтому оператор присваивания перемещения (который принимает параметр std::unique_ptr как параметр) также не рассматривается.

Ответ 2

Я пытаюсь понять, почему std::unique_ptr<MyClass> p = new MyClass; не работает

По той же причине, что и @songyuanyao, где она объявлена explicit, сообщает, что вы все еще можете инициализировать ее в другой форме инициализации, которая превосходит explicit:

// Valid, since now it 'explicit'
std::unique_ptr<MyClass> p { new MyClass{} };