Какие операции присваивания разрешены для std::unique_ptr?

Ответ

std::unique_ptr реализует семантику исключительного владения ресурсом. Поэтому разрешена только операция перемещающего присваивания. Копирующее присваивание и конструктор копирования явно удалены.

Разрешенные сценарии:

  1. Присваивание из временного объекта (rvalue) с помощью std::move:

    std::unique_ptr<int> ptr1 = std::make_unique<int>(42);
    std::unique_ptr<int> ptr2;
    ptr2 = std::move(ptr1); // OK: владение передается от ptr1 к ptr2
    // Теперь ptr1 == nullptr, ptr2 владеет int(42)
  2. Присваивание результата std::make_unique или другой фабричной функции:

    std::unique_ptr<MyClass> ptr;
    ptr = std::make_unique<MyClass>(); // OK: перемещение из временного объекта

Запрещенные сценарии:

std::unique_ptr<int> a = std::make_unique<int>(5);
std::unique_ptr<int> b = a; // Ошибка компиляции: конструктор копирования удален
b = a; // Ошибка компиляции: оператор копирующего присваивания удален

Это гарантирует, что в любой момент времени только один unique_ptr владеет конкретным динамическим объектом, что предотвращает двойное удаление и упрощает рассуждение о владении.

Ответ 18+ 🔞

А, ну ты про этот твой unique_ptr спрашиваешь? Ну, слушай, история-то проще пареной репы, если разобраться. Представь себе, что у тебя есть одна конкретная, едрид-мадрид, кружка. И ты — её единственный и неповторимый владелец. Ты можешь её кому-то отдать (ну, типа, сунуть в руки и забыть), но ты не можешь её скопировать волшебным образом, чтобы у тебя и у соседа была одна и та же физическая кружка. Так вот std::unique_ptr — это и есть та самая кружка, только для кусков памяти в хипе. Владение исключительное, блядь, один хозяин.

Поэтому с ним можно делать только одно — перемещать. Как будто ты говоришь: «На, держи, это теперь твоя головная боль». Исходный указатель после этого становится пустым, нихуя не указывает. Это и есть операция перемещающего присваивания.

Что работает (и это окей):

  1. Передал владение другому с помощью std::move (типа «на, забирай, мне похуй»):

    std::unique_ptr<int> ptr1 = std::make_unique<int>(42);
    std::unique_ptr<int> ptr2;
    ptr2 = std::move(ptr1); // Всё норм, владение переехало от ptr1 к ptr2
    // Теперь ptr1 — пустой, как твои обещания в понедельник, а ptr2 владеет этой памятью.
  2. Присвоил прямо результат создания (make_unique), это же временный объект, его и так скоро удалят:

    std::unique_ptr<MyClass> ptr;
    ptr = std::make_unique<MyClass>(); // Окей, создали и сразу передали тебе.

Что НЕ работает (и щас получишь по шапке от компилятора):

std::unique_ptr<int> a = std::make_unique<int>(5);
std::unique_ptr<int> b = a; // Ошибка компиляции: ёпта, ты чё, клонировать собрался? Конструктор копирования удалён!
b = a; // Ошибка компиляции: опять? Оператор копирующего присваивания тоже удалён, я же сказал!

Весь смысл в этом и заключается, чувак. Чтобы в любой момент времени было ясно, какого хуя и кто отвечает за удаление объекта. Никаких двойных удалений, когда два дурака пытаются освободить одну память, никакой путаницы. Один владелец — одна ответственность. Красота, ёпта.