Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any benefit in using std::forward instead of std::move to initialize an object?

Tags:

c++

c++17

I have a function template that takes an argument of some callable type, and uses std::bind to make a new callable object with predefined argument values for the original callable. I've written it with a forwarding reference parameter and std::forward, like this:

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

The cppreference documentation for std::bind says that the bound object "holds a member object of type std::decay<F>::type constructed from std::forward<F>(f)". Since std::bind forwards the function to its internal data member, forwarding the same function to the std::bind call in my own code seems reasonable and appropriate.

However, it's not clear what benefit that brings. If F is a reference type, std::decay removes the reference, so the bind object is going to store its own instance of the callable type. That instance will be constructed as a move if F is an rvalue reference, or a copy if F is an lvalue, and I can get the same result if I write my function like this:

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

Now my function's own f parameter will be initialized by either move or copy depending on how the function is called, but either way I now have my own instance of the function object, which I can move into the bind object.

The latter way seems simpler, but I wonder if I'm missing something — especially since the same reasoning would apply to std::bind itself, yet it takes an F&& and forwards it, instead of taking an F by value and moving it. Is there a disadvantage in doing it that way? Something that I'm not seeing?

like image 758
Wyzard Avatar asked Jul 08 '19 05:07

Wyzard


1 Answers

Using a forwarding reference and std::forward you can eliminate the creation of an extra object.

If you don't use a forwarding reference, you have three objects involved:

  1. Caller's original object
  2. Function parameter f, constructed using the copy or move constructor as appropriate
  3. Bind object's internal object, constructed by move constructor

If you use a forwarding reference with std::forward, you eliminate the second one. There will only be two objects created:

  1. Caller's original object
  2. Bind object's internal object, constructed using the copy or move constructor as appropriate

While move-constructing an object may be cheaper than copy-constructing (depending on the type), it still contributes some overhead that perfect-forwarding can avoid.

like image 75
Miles Budnek Avatar answered Oct 21 '22 11:10

Miles Budnek