Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ closure creation

#include <iostream>
#include <algorithm>
#include <vector>

int main()
{
  // Block 1
  {
    auto inc = []() { int i = 0; return [&]() { return i++; }; }();
    std::vector<int> v(10, 10);
    std::generate(v.begin(), v.end(), inc);
    for (auto i : v) std::cout << i << std::endl;
  }

  // Block 2
  {
    auto inc = []() { int i = 0; return [&]() { return i++; }; };
    std::vector<int> v(10, 10);
    std::generate(v.begin(), v.end(), inc());
    for (auto i : v) std::cout << i << std::endl;
  }
}

I am not sure why these two blocks produce different results.

Block 1: 32767 ... 32776
Block 2: 0 ... 10

And for std::generate the generator(inc) is passed by value, so I believe it should be fine, right?

I am running OS X.

Thanks,


Note that the results from above code are undefined, see below.
like image 847
Lin Avatar asked Sep 20 '17 15:09

Lin


People also ask

What is C closure?

A closure is a kind of object that contains a pointer or reference of some kind to a function to be executed along with the an instance of the data needed by the function.

Does C support closure?

Although C was created two decades after Lisp, it nonetheless lacks support for closures.

What is a closure in CS?

In computer science, a closure is a function that has an environment of its own. In this environment, there is at least one bound variable (a name that has a value, such as a number). The closure's environment keeps the bound variables in memory between uses of the closure.

How does a function create a closure?

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function.


1 Answers

I am not sure why these two blocks produce different results.

Both are undefined behavior, so the question is moot. In either case, we have a lambda like:

auto f = []() { int i = 0; return [&]() { return i++; }; };

And f() returns a lambda that has a dangling reference: i is destroyed at the end of the call to f. It's immaterial when that dangling reference happens - whether it happens long before the generate() call or into the generate() call.

If you want to make a generating counter with a lambda, the direct way is to make the lambda mutable and use init-capture:

auto inc = [i=0]() mutable { return i++; };

The mutable is required because lambdas are const by default, and we need to directly modify the member i.


The above is C++14 (due to the generalized init-capture). We could make this work in C++11 by simply changing the nested-lambda structure from the inner lambda capturing by reference to capturing by value:

auto inc = []{ int i = 0; return [=]() mutable { return i++; }; }();
//                               ~~~   ~~~~~~~

That's... kind of obnoxious, but it works?

like image 178
Barry Avatar answered Oct 22 '22 01:10

Barry