There are so many talk about the new C++ forwarding references. However sometimes in a particular situation it's still not clear to me whether they provide any advantage or not.
It's clear that passing heavy state functors (just like random number generators) by value is not a good idea. So let's use references. Okay, but...
...is there any advantage of using forwarding references like
template <class T, class Functor>
T func(T x, Functor &&f)
{
T y;
// do some computations involving f(x) and store it in y
return y;
}
instead of const references
template <class T, class Functor>
T func(T x, const Functor &f)
{
T y;
// do some computations involving f(x) and store it in y
return y;
}
in functions that take functor objects without forwarding them?
The main aspect of the question is the performance consideration.
The choice depends on what f
does, whether it makes sense to accept an rvalue, whether the function object is expected to be small, and whether reference semantics makes sense. Most standard library algorithms take function objects by value, because they are expected to have little to no state, and taking by value can be more efficient.
A good example for taking forwarding references is std::shuffle
:
template< class RandomIt, class URNG >
void shuffle( RandomIt first, RandomIt last, URNG&& g );
The URNG has to be taken by reference, as 1) it's not required to be copyable and 2) you really don't want to copy it (both because RNGs can be pretty expensive to copy and because users usually want shuffle
to change the RNG's state; they would be pretty surprised if two calls to shuffle
with the same RNG object resulted in the same "random" order). You also can't take it by const reference, because then you can't call operator()
on it - generating a random number changes the RNG's state.
The choice is then between a non-const lvalue reference, URNG&
and a forwarding reference, URNG&&
. Since sometimes you do want to accept an rvalue URNG - for instance when you want to shuffle to the same reproducible order, so you create a generator with a fixed seed at the call point - the latter was chosen.
The forwarding reference version is able to bind either to Functor
s with operator()
declared const or non const.
Here is an example that compiles on forward reference but not const reference:
template <class T, class Functor>
T func(T x, Functor &&f)
{
T y;
y = f(x);
return y;
}
template <class T, class Functor>
T func2(T x, const Functor &f)
{
T y;
y = f(x);
return y;
}
int main()
{
int a = 1;
auto add_a = [=](int x) mutable { return ++a; };
func(0, add_a); // compile
func2(0, add_a); // does not compile
}
http://coliru.stacked-crooked.com/a/3eeb21a57d66452b
Because you mutable
lambda does not have operator const (int)
In other words, invoke a mutable lambda will mutate it, however you cannot mutate const reference. So you need to use forward reference.
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