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?
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:
f, constructed using the copy or move constructor as appropriateIf you use a forwarding reference with std::forward, you eliminate the second one. There will only be two objects created:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With