8

I have the following function

template<class Function>
void f(Function&& g) {
    h(..., [&g](){g();});
}

It's a function f accepting a function, a lambda or a functor as argument. Inside it calls the function h to which I pass a lambda as argument that is calling g and receives g by capture.

  1. Should I pass g or &g in the capture field of the lambda ?
  2. Will the functor be copied with the above code ?
chmike
  • 20,922
  • 21
  • 83
  • 106
  • 1
    Normally function-like objects are passed by value. Copying them should be extremely cheap. If your objects are not like this, you are probably doing something not entirely right. – n. m. could be an AI Jul 22 '15 at 16:21

3 Answers3

4

If you capture g by reference, that is, with the syntax &g shown in your snippet, no copy will be performed. This is the preferred way. You should only copy if the lambda might be called after f finishes, which potentially implies a destruction of the object g refers to.
In that case, forwarding could be cheaper though:

template<class Function>
void f(Function&& g) {
    h(…, [g=std::forward<Function>(g)] {g();});
}
Columbo
  • 60,038
  • 8
  • 155
  • 203
  • 2
    Note that here, you do a copy/move of the original `g`. see [perfectly-capturing-a-perfect-forwarder-universal-reference-in-a-lambda](http://stackoverflow.com/questions/31410209/perfectly-capturing-a-perfect-forwarder-universal-reference-in-a-lambda/31410880#31410880) – Jarod42 Jul 22 '15 at 19:44
  • 2
    @Jarod42 That was my intention. – Columbo Jul 22 '15 at 21:57
  • 1
    See @Dieter's answer if one needs an equivalent solution that works with c++11 – chmike Jul 23 '15 at 08:59
2

You might do this:

template<class Function>
void f(Function&& g) {
    Function f(std::move(g));
    int additional = 0;
    h(..., [&f, additional](){f(additional);});
}
  • 2
    This answer has the benefit to work with c++11. The answer of @Columbo requires c++14. I picked his answer as valid answer because it's the new standard way to do it. But I'll use your answer until I switch to c++14. Your contribution was helpful. – chmike Jul 23 '15 at 09:03
  • AFAICS, This way has no benefit compared to @chmikes original approach whatsoever. First of all, you `move` when you should `forward`. Secondly, `f` is captured by reference, and its lifetime doesn't exceed `g`'s, so actually `f` is superfluous. – Columbo Jul 23 '15 at 09:10
  • @columbo Apparently my compiler (4.9.2) requires that `f` is passed by reference. This code works. Not sure of the difference it would make to use `forward`. – chmike Jul 24 '15 at 10:10
  • @chmike …what about `g` being an lvalue reference to some object that's to be used after `f` is called? Remember, the parameter is a forwarding reference, not an rvalue one. And yeah, of course this code - after fixing the aforementioned bug - works, but introducing `f` serves no purpose at all. One could just capture `g` by reference and that's it. – Columbo Jul 24 '15 at 11:30
1

You are constructing a lambda that accepts no arguments and returns nothing. But you already have such a functor: g!. You should just forward it:

template<class Function>
void f(Function&& g) {
    h(..., std::forward<Function>(g));
}
Barry
  • 286,269
  • 29
  • 621
  • 977
  • 2
    I suspect that OP just shortened it, and in reality the lambda does more. – Ishamael Jul 22 '15 at 16:23
  • 4
    @Ishamael Then OP should put what OP wants to do in the question so that I don't have to guess. – Barry Jul 22 '15 at 16:25
  • 1
    As @Ishamael said, I stripped the code to it's simplest form. The lambda does much more than call `g()` and `g()` has arguments and return a result. You are right @Barry, I should have made it explicit so that the lambda isn't optimized out in the answers. – chmike Jul 23 '15 at 08:44