40

I'm doing a simple normalization on a vector (weights), trying to make use of STL algorithms to make the code as clean as possible (I realize this is pretty trivial with for loops):

float tot = std::accumulate(weights.begin(), weights.end(), 0.0);
std::transform(weights.begin(), weights.end(), [](float x)->float{return(x/tot);});

At present, tot is not visible to the anonymous function, so this doesn't compile. What's the best way of making a local variable visible to the anonymous function?

bd1
  • 505
  • 1
  • 5
  • 8

3 Answers3

60

You need a closure.

float tot = std::accumulate(weights.begin(), weights.end(), 0);
std::transform(weights.begin(), weights.end(), [tot](float x)->float{return(x/tot);});

In this case tot is captured by value. C++11 lambdas support capturing by:

  1. value [x]
  2. reference [&x]
  3. any variable currently in scope by reference [&]
  4. same as 3, but by value [=]

You can mix any of the above in a comma separated list [x, &y].

pmr
  • 58,701
  • 10
  • 113
  • 156
  • Great! Is there a good web/book reference on C++11 anonymous function usage in STL? A lot of what I found on the web was either outdated workarounds for anonymous functions, or random blog postings. – bd1 Aug 18 '11 at 00:00
  • 1
    @bd1 The wikipedia on C++0x is actually quite good. Also the last standard draft available to the public is called n3424. Unfortunately there are no books on C++0x yet. – pmr Aug 18 '11 at 00:03
  • If you make a closure with `[=]` and it makes copies of _all_ the variables in enclosing scope, does that include global variables? When does it stop going up the scope ladder? – Seth Carnegie Aug 18 '11 at 00:34
  • 3
    @Seth I should have been more explicit: Only entities that are actually (odr-)used inside the lambda-body are captured when a capture-default (`&` or `=`) is used. That means a copy of the global will only be made, if you access the global. See §5.1.2.11 for capture-defaults and §5.1.2.9 for the scoping rules. – pmr Aug 18 '11 at 00:57
12

The lambda can "capture" variables from the ambient scope:

[ ..., N, ... ](int a, int b) -> int  { return (a + b) * N; }
 ^^^^^^^^^^^^^  ^^^^^^^^^^^^     ^^^^
 captured vars  local params     ret.type

You can capture by value or by reference, and you can use the special syntax [=] and [&] to capture anything from the ambient scope, i.e. anything you actually end up using.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • What if I am parsing a lambda as a function parameter. It throws an error saying the lambda function is not the right type for the parameter. – Ari Seyhun Sep 21 '17 at 05:40
  • @Acidic: Since you cannot say the *type* of a lambda expression, it's very difficult to have *functions* whose parameter is a closure type. Typically, you would use function *templates* to deduce the type, or a type-erasing wrapper such as `std::function` if you need to manage heterogeneous collections of callables. – Kerrek SB Sep 21 '17 at 12:58
2

You need to add tot to the "capture list":

float tot = std::accumulate(weights.begin(), weights.end(), 0);
std::transform(weights.begin(), weights.end(), [tot](float x)->float{return(x/tot);});

Alternatively you can use a capture-default to capture tot implicitly:

float tot = std::accumulate(weights.begin(), weights.end(), 0);
std::transform(weights.begin(), weights.end(), [=](float x)->float{return(x/tot);});
Mankarse
  • 39,818
  • 11
  • 97
  • 141