С++ конструктор копирования вызывается при возврате

error: use of deleted function 'A::A(const A&)'
 return tmp;
        ^~~

Почему конструктор копирования вызывается только тогда, когда в A есть виртуальный деструктор? Как этого избежать?

struct B {};

struct A{
    std::unique_ptr<B> x;
    virtual ~A() = default;
};

A f() {
    A tmp;
    return tmp;
}

Ответ 1

virtual ~A() = default; это пользователь, объявленный деструктором. Из-за этого у A больше нет конструктора перемещения. Это означает return tmp; не может переместить tmp и, поскольку tmp не копируется, вы получаете ошибку компилятора.

Это можно исправить двумя способами. Вы можете добавить конструктор перемещения как

struct A{
    std::unique_ptr<B> x;

    A() = default; // you have to add this since the move constructor was added
    A(A&&) = default; // defaulted move
    virtual ~A() = default;
};

или вы можете создать базовый класс, который имеет виртуальный деструктор и наследовать от него, как

struct C {
    virtual ~C() = default;
};

struct A : C {
    std::unique_ptr<B> x;
};

Это работает, потому что A больше не имеет объявленного пользователем деструктора (да, C делает, но мы заботимся только о A), поэтому он все равно будет генерировать конструктор перемещения в A Важной частью этого является то, что в C нет удаленного конструктора перемещения, у него просто нет одного периода, поэтому попытка его перемещения приведет к копированию. Это означает, что C конструктор копирования вызывается в A неявно генерируется конструктор перемещения, так как C(std::move(A_obj_to_move_from)) будет копировать до тех пор, как он не имеет удаленный конструктор перемещения.