返回时调用C ++复制构造函数

Sob*_*uch 18 c++

error: use of deleted function 'A::A(const A&)'
 return tmp;
        ^~~
Run Code Online (Sandbox Code Playgroud)

为什么仅在其中存在虚拟析构函数时才调用复制构造函数A?如何避免这种情况?

struct B {};

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

A f() {
    A tmp;
    return tmp;
}
Run Code Online (Sandbox Code Playgroud)

Nat*_*ica 30

virtual ~A() = default;是用户声明的析构函数。因此,A不再具有move构造函数。这意味着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;
};
Run Code Online (Sandbox Code Playgroud)

或者您可以创建一个具有虚拟析构函数的基类,并从该类继承该基类

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

struct A : C {
    std::unique_ptr<B> x;
};
Run Code Online (Sandbox Code Playgroud)

之所以可行,A是因为不再有用户声明的析构函数(是的,C但是我们只关心A),因此它仍会在中生成move构造函数A。其中的重要部分是C没有删除的move构造函数,只是没有一个句点,因此尝试移动它会导致复制。这意味着 CA隐式生成的move构造函数中调用了copy构造函数,因为C(std::move(A_obj_to_move_from))只要没有删除的move构造函数,它将进行复制。

  • 最好遵循零/五规则。要么全部添加(复制ctor,移动ctor,副本分配,移动分配,析构函数),要么什么都不添加。在此示例中,它们都不是必需的。 (11认同)
  • @ 0x5453除非这是父类,否则OP希望派生类被正确销毁。如果您有多态性,则需要一个虚拟析构函数。 (3认同)
  • @Tzalumen不需要删除(因为这就是唯一指针为您执行的操作),但是需要虚拟析构函数,以便唯一指针不会具有UB。 (3认同)
  • @Tzalumen如果您通过指向`X`基类的指针来删除`X`类的对象,并且该基类没有虚拟dtor,则为未定义行为。不管析构函数做什么。 (3认同)
  • @Tzalumen如果您具有多态性,则必须**具有虚拟析构函数。如果您不这样做,则不会调用派生类的析构函数,并且您拥有UB。 (3认同)
  • @Everyone OP无需向我们展示其其余代码。让我们假设他们需要一个,并给他们一个答案,为什么它会破裂以及如何修复它。您可以通过对问题的评论来询问他们是否真的需要虚拟析构函数。 (2认同)