问题
I have various functions in my codebase that take a generic callable object and pass it to a series of nested lambdas before calling it. Example:
template <typename TF>
void interface(TF&& f)
{
nested0([/*...*/]()
{
nested1([/*...*/](auto& x)
{
nested2([&x, /*...*/]()
{
f(x);
});
});
});
}
Note that interface
is taking a callable object of type TF
by forwarding reference (previously known as universal reference). The callable object is usually a lambda with various captured variables, both by value and by reference.
What is the best (in terms of performance) way of capturing f
in the nested lambdas while maintaining correctness?
I can think of three options:
Capture
f
by copy.nested0([f]() { nested1([f](auto& x) { nested2([&x, f]() { f(x); }); }); });
Could cause unnecessary copies, and if the captured object is
mutable
it could cause incorrect behavior.Capture
f
by reference.nested0([&f]() { nested1([&f](auto& x) { nested2([&x, &f]() { f(x); }); }); });
Seems reasonable, but could cause problems if any of the nested lambdas perform an action that outlives the owner of
f
. Imagine ifnested2
's body was executed in a separate thread -f
could already be out of scope.Make the lambdas
mutable
and capture by perfect-forwarding.#define FWD(...) std::forward<decltype(__VA_ARGS__)>(__VA_ARGS__) nested0([f = FWD(f)]() mutable { nested1([f = FWD(f)](auto& x) mutable { nested2([&x, f = FWD(f)]() mutable { f(x); }); }); });
The lambdas have to be
mutable
because we're potentially movingf
from a lambda to another one. This approach seems to avoid unnecessary copies and to correctly move the callable object if it needs to outlive the original caller.
Is option 3 always the best one, or does it have any potential drawback? ...or maybe there is no "best and correct" way at all (knowledge about the callable object is required)?
回答1:
As mentioned in the comments, it's difficult to say with such a poor context given about the problem.
That said, the best solution seems to me to capture everything by reference and break this logic whenever you need a copy the aim of which is to outlive the lifetime of f
, as an example:
nested0([&f]() {
n1([&f](auto& x) {
n2([&x,
// get a local copy of f
f{std::forward<TF>(f)}]() {
f(x);
});
});
});
Anyway, there is no rule of thumb for such a problem.
The best solution is tightly bound likely to the actual problem.
As usual. Fair enough.
来源:https://stackoverflow.com/questions/36792059/capturing-generic-callable-objects-in-nested-lambdas-always-forward