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

时间:2014-11-26 00:33:07

标签: c++ c++11 move-semantics move-constructor

我正在玩移动构造器和移动作业,我偶然发现了这个问题。第一个代码:

#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函数中的move构造函数创建的临时对象。这样做会使foo对象的值等于零。不幸的是它接触Bar函数中的对象作为参数是某种参考,因为它不会“移动”原始值,但使用Bar的参数我可以改变它。

有人会介意我在控制台看到我为什么会这样做:

#5
5

而不是

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

4 个答案:

答案 0 :(得分:18)

右值引用确实是(惊讶:)一个引用

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

所以,如果你不从它移动,你实际上将操作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&amp;&amp;只是作为一个传统的参考

答案 1 :(得分:7)

当你写value = std::move(other.value);时,你应该明白std::move不会移动任何东西。它只是将其参数转换为右值参考,然后,如果左侧有一个移动构造函数/赋值运算符,左侧将处理它(以及如何实际移动对象)。对于普通旧类型(POD),std::move并没有真正做任何事情,因此旧值保持不变。你不是&#34;身体&#34;移动任何东西。

答案 2 :(得分:2)

比较这两个函数:

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

VS

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

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

# 5
5

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

# 5
1

DEMO


编辑:这是我前段时间提出的一个问题。我的错误是假设rvalue引用的创建直接调用了移动构造函数的调用。但是,如前所述,std::move在运行时没有做任何事情,它只是将类型更改为rvalue-reference。移动构造函数仅在您移动时才会调用&#34;你的左值引用如上所述另一个对象。

答案 3 :(得分:1)

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

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

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

Bar(  std::move( x )  );

调用复制构造函数

Bar( x );