【问题标题】:Is it safe to pass std::function<bool(std::string)> &&callback (i.e. as a rvalue move) and what is the effect?传递 std::function<bool(std::string)> &&callback 是否安全(即作为右值移动),效果是什么?
【发布时间】:2018-08-29 10:30:09
【问题描述】:

给定以下工作代码(main.cpp):

#include <functional>
#include <iostream>

struct worker
{
   std::function<bool(std::string)> m_callback;
   void do_work(std::function<bool(std::string)> callback) // <--- this line
   {
      m_callback = std::bind(callback, std::placeholders::_1);
      callback("hello world!\n");
   }
};


// pretty boring class - a cut down of my actual class
struct helper
{
   worker the_worker;
   bool work_callback(std::string str)
   {
      std::cout << str << std::endl;
      return false;
   }
};

int main()
{
   helper the_helper;
   the_helper.the_worker.do_work( [&](std::string data){ return the_helper.work_callback(data); });
}

编译:-std=c++11 -O2 -Wall -Wextra -pedantic-errors -O2 main.cpp

我已经评论了有问题的行(&lt;-- this line - 第 7 行附近),我认为在其中使用会更有效:void do_work(std::function&lt;bool(std::string)&gt;&amp;&amp; callback) 即使用 &amp;&amp; 移动语义。

我从来没有真正使用过这个,主要是因为我还不太了解它。

我的理解是这样的:

void do_work(std::function&lt;bool(std::string)&gt; callback) - 将获取我传入的 lambda 的副本(我认为这是一个右值)。

void do_work(std::function&lt;bool(std::string)&gt; callback) - 将移动我传入的 lambda,因为它是一个右值。

我对右值的粗略想法是任何临时变量。

问题:

  1. 我不是 100% 清楚的是,我写的是正确的吗?因此使用&amp;&amp; 是否安全。两者似乎都有效。

  2. 如果不是像这样传递 lambda,这个 &amp;&amp; 方法是否也有效:

the_helper.the_worker.do_work( [&amp;](std::string data){ return the_helper.work_callback(data); });

我们传入 std::bind(...):

the_worker.do_work(std::bind(&amp;helper::work_callback, the_helper, std::placeholders::_1));

【问题讨论】:

  • 您不需要std::bind 调用,只需分配m_callback = callback;
  • @Someprogrammerdude 我知道 - 你必须原谅这个糟糕的例子! (很难做一个简单的例子来展示一个点而不做废话)......但我确实想以这种方式传递和存储回调(即使用 lambda 或绑定)。我只是真的想专注于传递类型(即按值或按&amp;&amp;)。

标签: c++ c++11 move-semantics rvalue-reference


【解决方案1】:

如果参数被定义为右值引用,则必须传递一个临时值或将左值强制转换为右值,例如std::move()

并且右值引用的语义是调用者应该期望传递的参数被掠夺,使其有效但任意,这意味着大部分无用。

但是接收右值引用的函数,尽管有掠夺的许可,但没有任何掠夺的义务。如果它没有明确地这样做,例如通过该许可证,那么它就不会通过,也不会发生任何特别的事情。

你的代码就是这样。

虽然我会从我的词汇表中禁止std::bind,但使用与否实际上并没有任何显着差异。

【讨论】:

  • 我在反复阅读你写的文字,但我觉得我太简单了,看不懂!我想我理解你对std::move() 所说的话——这意味着如果它是一个左值(比如一个对象或类似的东西?)那么你就移动它。您能否像我还是个孩子一样解释最后一部分,例如:在我的代码中的两种情况下会发生什么(即它实际上是否被移动了?)。谢谢:)
  • 不,它没有被移动,因为即使你在 some 点使用右值引用,你也不会在任何会导致行为改变的地方使用它们,也就是导致对象被移动。
  • 出于好奇,您对std::bind 有什么问题?您是指它的两种/所有用法,还是仅指传递函数的用法(即第二个)?
  • @code_fodder:我对std::bind 的问题是 lambda 既更干净,有时可能更高效。诚然,std::bind 具有为相同参数重复生成相同类型的优势,而不是对每个案例都是唯一的。
  • 好的,谢谢,我只是在检查它没有什么不好的地方——你只是说它被 lambdas 有效地淘汰了:)
【解决方案2】:

在这种情况下,无论您是按值传递还是通过 rval ref 传递,都必须创建一个临时的 std::function,这是因为 lambda 并不是真正的 std::function。在任何情况下,您都应该在分配之前移动 std::function,以避免进行不必要的复制。
我建议在这种情况下按值传递,因为这更灵活一些,如果你传递 lambda,那么它不会造成任何伤害,因为 std::function 通常会在适当的位置构造(所以临时不会被移动到函数中;这个移动可以并且通常会被省略)。

【讨论】:

  • 好的,所以,一般来说,通过副本传递 lambda 只是更简单/灵活的方法,我们不使用 rvalref?
  • 您实际上并没有在这里传递 lamda,而是使用它来创建一个临时 std::function 对象,然后将其传递给函数。所以这里可能会发生几件事,例如临时将被创建,然后移动到函数中,或者 std::function 将被创建到位。在实践中,第二个更有可能发生,如果你通过 rval ref 会发生同样的事情。
猜你喜欢
  • 2021-11-07
  • 2016-10-26
  • 1970-01-01
  • 1970-01-01
  • 2021-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多