Typically I use the following pattern when accepting a lambda as an argument to a function (A template class passed-by-value):
template <class Function>
void higherOrderFunction(Function f) {
f();
}
Does this copy (the closure of) the argument? If so, is there anything wrong with accepting the lambda by const reference instead?
template <class Function>
void higherOrderFunction(const Function& f) {
f();
}
A simple test seems to indicate that this works fine, but I want to know if there are any special considerations that I should be aware of.
The STL does not use references for function objects in parameters of templates. So, most probably, neither should you, unless you have very specific needs and understand in depth why a ref will help you in your case, and what are the risks when you do so.
If you do use references, I'm not sure the end result would be easy to read and extremely maintainable though, so maybe even then you should rather find alternative solutions, like e.g. maintaining your state in a reference-captured var, and continue to pass your lambda object by value. Otherwise, please at least add some comments indicating why you did something complicated and non-idiomatic, instead of the simple thing the standard uses all over the place in its own library.
Anyway there are places where perfectly forwarded universal references would be perfectly fine and with small risk of extra complexity: if the function object is simply forwarded once to a param of another function. And well, that's why you should stick to the simplest possible stuff unless you understand in great depth (and you expect that all the future maintainers of your code also will); because blindly using std::forward on an universal ref is not safe in the general case: for example it would be extremely dangerous to use that in the body of a loop. And even then, you probably only want to do that when you expect the future maintainers will also all known that and not extend the code in a seemingly simple but incorrect way.
So as a general rule; only use particular tricky C++ feature if you know extremely well why you use them, all the details of how they work, and all the potential risks associated. Properly using C++ references in C++ is tricky because if you do any mistake (among hundreds of potential ones), the compiler will happily generate a binary for your broken program, most of the time with not even a warning telling you you have done some shit. Passing by value is way less dangerous, so at least in contexts where the standard does so, so should you. Unless you are an expert, and only work with experts.
A copy is a copy, so you cannot mutate the original, might have some performance impact when a lot of data is involved:
See it in action.
Don't forget: lambdas are mere syntactic sugar for callable classes. (But extremely helpful)
If you pass by value you will copy the closure object (assuming you don't define the lambda inline, in which case it will be moved). This might be undesirable if the state is expensive to copy, and will fail to compile if the state is not copyable.
If you pass by
const
reference, then you cannot pass amutable
lambda that modifies its data members as the argument tohigherOrderFunction()
because amutable
lambda has a non-const
operator()
, and you cannot invoke that on aconst
object.The best option is to use a forwarding reference. Then
higherOrderFunction
can accept either lvalues or rvalues that the caller passes.This allows the simple cases as well as the ones mentioned above to compile. For a discussion of why
std::forward
should be used, see this answer.Live demo