使用std :: forward代替std :: move初始化对象有什么好处?

时间:2019-07-08 05:40:19

标签: c++ c++17

我有一个函数模板,该模板接受某种可调用类型的参数,并使用std::bind来创建一个新的可调用对象,并为原始可调用对象预定义参数值。我已经使用转发参考参数和std::forward编写了它,如下所示:

template <typename F>
auto make_example_caller(F &&f) {
  return std::bind(std::forward<F>(f), 123, 456, 789);
}

cppreference documentation for std::bind说绑定对象“持有从std::decay<F>::type构造的类型std::forward<F>(f)的成员对象”。由于std::bind将函数转发给其内部数据成员,因此在我自己的代码中将相同的函数转发给std::bind调用似乎是合理且适当的。

但是,尚不清楚带来什么好处。如果F是引用类型,则std::decay删除引用,因此绑定对象将存储其自己的可调用类型的实例。如果F是一个右值引用,则该实例将被构造为移动;如果F是一个左值,则该实例将被构造为副本,如果我这样编写函数,则可以获得相同的结果:

template <typename F>
auto make_example_caller(F f) {  // Note, no &&
  return std::bind(std::move(f), 123, 456, 789);  // move, not forward
}

现在,根据调用函数的方式,将通过移动或复制来初始化函数自己的f参数,但是无论哪种方式,我现在都有自己的函数对象实例,可以将其移入绑定中对象。

后一种方法似乎更简单,但是我想知道是否遗漏了某些东西-尤其是因为相同的推理将适用于std::bind本身,但是它需要F&&并将其转发,而不是采用F(按值)并将其移动。这样做有缺点吗?我没看到的东西?

1 个答案:

答案 0 :(得分:6)

使用转发参考和std::forward,您可以避免创建额外的对象。

如果不使用转发引用,则涉及三个对象:

  1. 呼叫者的原始对象
  2. 函数参数f,使用适当的copy或move构造函数构造
  3. 绑定对象的内部对象,由移动构造函数构造

如果您将转发引用与std::forward一起使用,则将消除第二个引用。将仅创建两个对象:

  1. 呼叫者的原始对象
  2. 绑定对象的内部对象,使用适当的copy或move构造函数构造

虽然移动构造对象 可能比复制构造便宜(取决于类型),但它仍会带来一些无法完全完善的开销。