为什么显式调用基类移动构造函数实际上会调用基类复制构造函数?

3

我试图通过派生类的移动构造函数显式调用基类的移动构造函数,但是,惊奇!,实际上调用了基类的复制构造函数而不是基类的移动构造函数。

我使用std::move()函数来确保调用了派生类的移动构造函数!

代码:

class Base
{
public:
    Base(const Base& rhs){ cout << "base copy ctor" << endl; }
    Base(Base&& rhs){ cout << "base move ctor" << endl; }
};

class Derived : public Base
{
public:

    Derived(Derived&& rhs) : Base(rhs) { cout << "derived move ctor"; }
    Derived(const Derived& rhs) : Base(rhs) { cout << "derived copy ctor" << endl; }
};

int main()
{
    Derived a;
    Derived y = std::move(a); // invoke move ctor
    cin.ignore();
    return 0;
}

程序输出:

基类复制构造函数

派生类移动构造函数

如您所见,基类的移动构造函数被忘记了,那么我该如何调用它呢?

3个回答

5
在您的“Derived”类上下文中,参数“rhs”显然有一个名称。因此,它必须是左值,不能是右值。但是,“T&&”只绑定到右值。如果您想调用基类的移动构造函数,您需要使用以下代码:
Derived(Derived&& rhs): Base(std::move(rhs)) { std::cout << "derived move ctor"; }

这将调用Base的移动构造函数,并移动rhsBase部分。由于Base不知道任何关于Derived成员的信息,因此Base的移动构造函数不会移动由Derived添加的任何内容。


如果我在主函数中写入以下内容:Derived y = Derived();,同时忘记使用std::move会怎么样?我已经尝试过了,在这种情况下,我没有得到任何程序输出,那么真正被称为什么呢? - codekiddy
@codekiddy:在示例Derived y = Derived()中,概念上调用了移动构造函数:右侧的对象无法被引用,即它不是左值。然而,大多数编译器可能会省略移动构造:如果编译器可以直接在正确的位置构造临时对象,则可以自由地不使用移动(或复制)构造,即使在省略构造函数时会有副作用(例如从构造函数或析构函数输出)。 - Dietmar Kühl
为了移动rhs的基础部分,我被迫在派生的移动构造函数中使用Base(std::move(rhs)),有没有避免使用std::move的方法?非常感谢。 - codekiddy
@codekiddy:当然,你可以让派生类构造函数隐式生成或者显式地拥有它们的默认行为:Derived(Derived&&) = default;。但是,一旦你明确地将参数转发到Base()构造函数,你就需要使用std::move()(或等效物)。 - Dietmar Kühl
好的,非常感谢你和其他人! - codekiddy

2

构造函数或者其他带有 && 的函数或方法只有在以下两个条件同时满足时,编译器才会选择它:

  • 你传入的表达式的数据类型必须是 T&& 或者 T。- 也就是说,T& 是不被接受的
  • 并且 它必须是一个右值 - 比如从函数中返回(通过值 T 或者 T&&)。

move(rhs) 满足这两个条件。 rhs 是正确的类型,但它必须先从函数(如 move)返回才能被视为符合要求,可以传递给需要 && 的函数。


1
如果使用基类移动构造函数,则派生构造函数可以访问已移动的对象。这是很危险的,因此除非您明确告诉编译器您已经完成了对该对象的使用并且它是安全可移动的,否则不会发生这种情况。

仅仅因为移动构造函数而存在危险并不是什么特别的事情。原因在于,当写入Base(rhs)时,使用左值调用了Base的构造函数,而左值永远不会绑定到右值引用:你必须显式地将左值转换成右值。 - Dietmar Kühl
@DietmarKühl:它被归类为lvalue,因为它可以在以后被访问。 - Ben Voigt
哦,你想避免使用lvalue和rvalue这两个术语吗?...因为这是它们之间的重要区别。 - Dietmar Kühl
@DietmarKühl:并不是我想要避免它们,而是您已经解释得非常好了。我只是想强调为什么这是从类型安全的角度来看正确的行为,而不仅仅是“因为标准规定如此”。 - Ben Voigt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接