6

I'm learning how to use C++ lambda functions along with <functional>'s function class. I am trying to solve this Code Golf as practice (challenge is Curry for Dinner)

I have this function:

// This creates a function that runs y a number of 
// times equal to x's return value.
function<void()> Curry(function<int()> x, function<void()> y)
{
    return [&]() {
        for (int i = 0; i < x(); i++)
        {
            y();
        }
    };
}

To test this I have this code in my main():

auto x = [](){ return 8; };
auto y = [](){ cout << "test "; };
auto g = Curry(x, y);

This throws Access violation reading location 0xCCCCCCCC. in Functional.h.

Yet when I copy-paste the lambda function from inside Curry() to inside my main like this:

auto x = [](){ return 8; };
auto y = [](){ cout << "test "; };
auto g = [&]() {
    for (int i = 0; i < x(); i++)
    {
        y();
    }
};

I get the code running as expected. Why does this happen?

Community
  • 1
  • 1
einsteinsci
  • 1,137
  • 1
  • 8
  • 22
  • I'm too lazy to answer the others, but [here's Curry for Dinner in C++14](http://coliru.stacked-crooked.com/a/7d46f072f30d2c5d "Live at Coliru"): `auto Curry=[](auto x,auto y){return[=]{for(auto i=x();i--;y());};};` – Casey Apr 30 '14 at 20:43

1 Answers1

9

You have a few problems.

Here:

  return [&]() {

you capture by reference. Any variables you capture has to have a lifetime that exceeds your own. It means that running the lambda becomes undefined behavior after the variables you capture&use lifetime ends. As you are returning this lambda, and capturing local state, this seems likely to happen. (Note I said variables -- due to a quirk in the standard, [&] captures variables not the data referred to by variables, so even capturing & function arguments by [&] is not safe. This may change in future revisions of the standard... There are neat optimizations that this particular set of rules allow in lambda implementations (reduce [&] lambdas to having 1 pointer worth of state(!)), but it also introduces the only case in C++ where you have a reference to a reference variable in effect...)

Change it to

  return [=]() {

and capture by-value.

Or even:

  return [x,y]() {

to list your captures explicitly.

When using a lambda which does not outlive the current scope, I use [&]. Otherwise, I capture by value explicitly the stuff I am going to use, as lifetime is important in that case.

Next:

    for (int i = 0; i < x(); i++)

you run x once for every loop iteration. Seems silly!

Instead:

    auto max = x();
    for (auto i = max; i > 0; --i)

which runs max times, and as it happens works if the return value of x was changed to unsigned int or whatever.

Or:

    int max = x();
    for (int i = 0; i < max; ++i)

which both runs x once, and behaves better if x returns -1.

Alternatively you can use the obscure operator -->:

    int count = x();
    while( count --> 0 )

if you want to make your code unreadable. ;)

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Even changing the parameters to references (because the objects do live long enough) may not help. Further detail at http://stackoverflow.com/q/21443023/103167 – Ben Voigt Apr 30 '14 at 18:33
  • @BenVoigt yep, the lifetime of the reference does not exceed the scope. The lifetime of the referenced data does. I guess I should be clear that it is variables, not data, I am talking about. – Yakk - Adam Nevraumont Apr 30 '14 at 18:34
  • @Yakk: I don't think it's a quirk or subject to change; it allows a very sensible scheme where lambda capture-by-reference is implemented by storing a copy of the stack pointer. All discussed in that linked question. – Ben Voigt Apr 30 '14 at 18:37
  • @BenVoigt yep, I get it. I can still call it a quirk, even if it opens up optimization opportunities. – Yakk - Adam Nevraumont Apr 30 '14 at 18:38
  • 1
    Why not `for (auto i = x(); i > 0; --i)` ? Extra verbosity doesn't seem to be necessary – SomeWittyUsername May 01 '14 at 03:03
  • @icepack first, that is no shorter than my `-->` version ;) (after renaming `count`). Second, I find storing the `max` of a loop or iteration worthwhile for debugging, and in release builds its existence gets optimized away anyhow. – Yakk - Adam Nevraumont May 01 '14 at 14:09