为什么在返回时将复制构造函数而不是移动构造函数

Why is the copy constructor called instead of the move constructor when returning?

本文关键字:构造函数 移动 复制 返回 为什么      更新时间:2023-10-16

假设我的类MyClass具有正确的移动构造函数,并且删除了复制构造函数。现在我要返回这样的课程:

MyClass func()
{
    return MyClass();
}

在这种情况下

现在说MyClass具有<<操作员的实现:

MyClass& operator<<(MyClass& target, const int& source)
{
    target.add(source);
    return target;
}

当我更改上述代码时:

MyClass func()
{
    return MyClass() << 5;
}

我遇到了编译器错误,因此无法访问复制构造函数,因为它已删除。但是为什么在这种情况下完全使用复制构造函数?

现在,我正在通过这样的lvalue返回此类:

MyClass func()
{
    return MyClass();
}

no,返回的表达式为 xvalue (一种rvalue(,用于初始化返回结果的结果(由于C 17,情况有些复杂,但是这仍然是它的要旨;此外,您在C 11(。

在这种情况下

确实;rvalue将初始化rvalue参考,因此整个内容都可以匹配移动构造函数。

当我更改上述代码时:

&hellip;现在的表达式为MyClass() << 5,它具有MyClass&类型。这从来都不是rvalue。这是一个lvalue。这是指现有对象的表达式。

因此,如果没有明确的std::move,则可以将其用于 copy-initialise 结果。而且,由于您的复制构造函数已被删除,因此无法使用。


我完全感到惊讶的是示例编译,因为临时性不能用来初始化lvalue参考(您的操作员的第一个参数(,尽管已知某些工具链(MSV(接受了它作为扩展。bk_hr>

然后将返回std::move(MyClass() << 5);工作?

是的,我相信。

但是,这很奇怪,并使读者进行了仔细检查,以确保没有悬而未决的参考。这表明有一种更好的方法可以实现更清晰的代码:

MyClass func()
{
    MyClass m;
    m << 5;
    return m;
}

现在,您仍然在没有任何奇怪的滑稽动作的情况下移动(因为这是return局部变量时的特殊规则(。而且,作为奖励,<<呼叫完全符合标准。

您的操作员返回MyClass&。因此,您正在返回一个lvalue,而不是可以自动移动的rvalue。

您可以通过依靠NRVO的标准保证来避免副本。

MyClass func()
{
    MyClass m;
    m << 5;
    return m;
}

这将完全列出对象,或者移动它。所有这些都因为它是函数本地对象。


另一个选项,在您尝试在rvalue上调用 operator<<时,是在rvalue参考文献中提供过载。

MyClass&& operator<<(MyClass&& target, int i) {
    target << i; // Reuse the operator you have, here target is an lvalue
    return std::move(target);
}

将使MyClass() << 5本身形成良好(请参阅另一个答案(,并返回可以构造返回对象的XVALUE。虽然通常看不到operator<<的超载。

您的operator<<将其第一个参数作为非const参考。您不能将非企业引用绑定到临时性。但是MyClass()将新创建的实例作为临时返回。

另外,当func返回值时,operator<<返回参考。那么,除了返回副本外,还能做什么?