【问题标题】:std::function move constructor not called未调用 std::function 移动构造函数
【发布时间】:2014-01-23 11:42:09
【问题描述】:

考虑这段代码:

void makeNew(std::function<void()>&& func)
{
    std::function<void()> p = func;
}

void test(){}

用法:

makeNew(std::bind(test));

预期结果:

std::bind(test) 的结果类型创建的临时std::function&lt;void()&gt; 对象 然后这个临时 std::function 对象作为右值引用传递给 makeNew,然后移动 std::function p = func 时调用的构造函数;被调用。

实际发生的情况(使用 VS2010 并使用调试器单步执行):

对于以下语句std::function&lt;void()&gt; p = func;,调用的是复制构造函数而不是移动构造函数。

有人可以解释这种行为吗?

【问题讨论】:

  • func 我是一个左值。您可以使用 std::move 将其转换为可以移动的右值。

标签: c++ c++11 constructor


【解决方案1】:

右值引用不是右值(除了从函数返回时)。它只是一种不同类型的左值,具有与普通引用不同的绑定规则。

如果您想从右值引用移动,请使用std::move,与任何其他引用相同。 std::move 返回一个右值引用类型,因此表达式 std::move(func) 是一个 xvalue,因此 一个右值并且可以从中移动。 func 是一个左值,即使它具有右值引用类型。

请注意,很少需要使用右值引用的函数。你这样做是为了排除左值,要么是因为你想完全阻止它们被传入,要么是因为你有一个做不同事情的左值重载。最常见的情况是您希望函数从右值移动或复制左值。因此,您可以改为按值获取参数并从中移动(如约瑟夫的回答),与编写两个不同函数的麻烦相比,开销很小或没有。

【讨论】:

    【解决方案2】:

    您错误地使用了右值引用。通过右值引用获取参数并不是一件很常见的事情。它没有说“我想从参数中移动”,也没有说“我想稍后从这个参数中移动”。相反,它是一个引用 - 没有任何内容被复制或移动,它只是被引用。在函数内部,表达式func是一个左值(因为被命名的东西都是左值),所以在用它初始化p时会调用复制构造函数。

    您应该做的是按值获取参数:

    void makeNew(std::function<void()> p)
    {
      // ...
    }
    

    如果你将一个右值传递给这个函数,它将被移动到函数中。如果你传递一个左值,它将被复制。看,现在不需要在makeNew 中添加额外的std::function。只需使用参数本身!如果您出于某种原因确实需要摆脱争论,那么您可以这样做:

    void makeNew(std::function<void()> func)
    {
      std::function<void()> p = std::move(func);
    }
    

    使用std::move 将表达式func(它是一个左值)转换为一个右值表达式。然后它将被移出。但是,除非您要进入另一个功能,否则我看不出有什么好的理由需要这样做。

    当然,万一我完全错了,而你真的想要一个右值引用,如果你想摆脱它,你仍然需要做std::move

    【讨论】:

    • 不是 std::move 只是强制转换为 && 吗?
    • @JobNick 右值引用类型变量的名称是左值。返回右值引用的函数是右值。 std::move 所依赖的就是这个小怪癖。
    • 非常有帮助的解释;我现在明白为什么“按值”声明 function 参数是有意义的。
    猜你喜欢
    • 2015-01-17
    • 2015-06-27
    • 2020-10-16
    • 2019-05-27
    • 2017-04-17
    • 2011-05-22
    • 2017-09-19
    • 1970-01-01
    • 2011-11-25
    相关资源
    最近更新 更多