将对象作为函数的参数发送时移动语义

Move semantics when sending object as function's parameter

本文关键字:移动 语义 参数 对象 函数      更新时间:2023-10-16

我正在使用移动构造函数和移动赋值,我偶然发现了这个问题。第一个代码:

#include <iostream>
#include <utility>
class Foo {
    public:
        Foo() {}
        Foo(Foo&& other) {
            value = std::move(other.value);
            other.value = 1; //since it's int!
        }
        int value;
    private:
        Foo(const Foo& other);
};

void Bar(Foo&& x) {
    std::cout << "# " << x.value << std::endl;
}
int main() {
    Foo foo;
    foo.value = 5;
    Bar(std::move(foo));
    std::cout << foo.value << std::endl;
    return 0;
}

在我看来,当我使用:

Bar(std::move(foo));

程序应该将foo对象"移动"到使用Bar函数中的移动构造函数创建的临时对象。这样做会使 foo 对象的值等于零。不幸的是,它接缝了 Bar 函数中作为参数保存的对象是某种参考,因为它不会"移动"原始值,但使用 Bar 的参数我可以更改它。

有人介意解释我为什么我在控制台中看到:

#5
5

而不是

#5
0 //this should be done by move constructor?

右值引用是(惊喜:)确实是一个参考

您可以从中移动,但std::move不会移动。

因此,如果您不离开它,您将实际对 rvalue 对象进行操作(通过 rvalue 引用)。


通常的模式是

void foo(X&& x)
{
    X mine(std::move(x));
    // x will not be affected anymore
}

但是,当您这样做时

void foo(X&& x)
{
    x.stuff();
    x.setBooDitty(42);
}

实际上,X&& 只是充当传统的参考

当你写value = std::move(other.value);时,你应该明白std::move不会移动任何东西。它只是将其参数转换为右值引用,然后,如果左侧有一个移动构造函数/赋值运算符,则左侧处理它(以及如何实际移动对象)。对于普通旧类型 (POD),std::move实际上不会执行任何操作,因此旧值保持不变。你没有"物理"移动任何东西。

比较这两个函数:

void Bar(Foo&& x) {
    std::cout << "# " << x.value << std::endl;
}

与。

void Bar(Foo&& x) {
    std::cout << "# " << x.value << std::endl;
    Foo y=std::move(x);
}

两者都采用右值引用,但只有第二个调用移动构造函数。因此,第一个的输出是

# 5
5

而第二个的输出 - 因为foo的值被改变 - 是:

# 5
1

演示


编辑:这是我前段时间遇到的一个问题。我的错误是假设创建右值引用直接调用移动构造函数的调用。但是,如前所述,std::move在运行时不执行任何操作,它只是将类型更改为右值引用。仅当您将右值引用"移动"到另一个对象(如上所述)时,才会调用 move 构造函数。

Bar 函数不应引用&&(此语法仅在移动构造函数声明/定义中有效):

void Bar(Foo x) { ... }  // not "Foo&& x"

在函数栏中调用临时参数对象的移动构造函数

Bar(  std::move( x )  );

调用复制构造函数

Bar( x );