C++ copy constructor called at return

virtual ~A() = default; is a user declared destructor. Because of that, A no longer has a move constructor. That means return tmp; can't move tmp and since tmp is not copyable, you get a compiler error.

There are two ways you can fix this. You can add a move constructor like


struct A{
    std::unique_ptr x;

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

or you can create a base class that has the virtual destructor and inherit from that like

struct C {
    virtual ~C() = default;

struct A : C {
    std::unique_ptr x;

This works because A no longer has a user declared destructor (Yes, C does but we only care about A) so it will still generate a move constructor in A. The important part of this is that C doesn't have a deleted move constructor, it just doesn't have one period, so trying to move it will cause a copy. That means C's copy constructor is called in A's implicitly generated move constructor since C(std::move(A_obj_to_move_from)) will copy as long as it doesn't have a deleted move constructor.