Bakkot
Bakkot

Reputation: 1245

c++11 capture-by-value lambda producing wrong value

I'm trying to store a lambda in an object system involving several layers of indirection. I'm using g++ 4.7.1.

Depending on how exactly I construct the (equivalent) objects, the lambda may or may not have the correct value.

Code:

#include <iostream>
#include <functional> // used for std::function

using namespace std; // TODO nope

typedef function<int()> intf;


struct SaveLambda {
    const intf func;
    SaveLambda(const intf& _func) : func(_func) {}  
};


struct StoreSaved {
    const SaveLambda* child;
    StoreSaved(const SaveLambda& _child) : child(&_child) {
        cout << "Before returning parent: " <<  child->func() << endl;
    }
};


int main() {
    const int ten = 10;

    auto S = SaveLambda([ten](){return ten;});
    cout << "No indirection: " << S.func() << endl << endl;

    auto saved = StoreSaved(S);
    cout << "Indirection, saved: " << saved.child->func() << endl << endl;

    auto temps = StoreSaved ( SaveLambda([ten](){cout << "&ten: "<< &ten << endl; return ten;}) );
    cout << "***** what. *****" << endl;
    cout << "Indirection, unsaved: " << temps.child->func() << endl;
    cout << "***** what. *****" << endl << endl;

    cout << "ten still lives: " << ten << endl;
}

Compile as g++ -std=c++11 -Wall -o itest itest.cpp and run: notice the one line of output with a different value.

What am I doing wrong? I assumed that capture-by-value would, well, capture by value. (Observe most disconcertingly that the print in StoreSaved (line 15) produces the correct value, unlike line 34, despite these both referring to the same object. The only difference is adding another layer of indirection.)

Upvotes: 2

Views: 580

Answers (2)

Gantmajer
Gantmajer

Reputation: 11

As already pointed out by others, the problem is that in temps you end with a pointer to a nonexistent SaveLambda struct, as it is a temporary.

You can keep a copy using a SaveLambda struct in StoreSaved, instead of a pointer:

struct StoreSaved {
   const SaveLambda child;
   StoreSaved(const SaveLambda& _child) : child(_child) {
       cout << "Before returning parent: " <<  child.func() << endl;
   }
};

You also have to change all the child->func() to child.func(), as you are not dealing with a pointer anymore.

Upvotes: 1

GManNickG
GManNickG

Reputation: 503805

This is wrong:

auto temps = StoreSaved(
                /* This temporary value dies at the last semicolon! */
                SaveLambda([ten](){cout << "&ten: "<< &ten << endl; return ten;})
                );

StoreSaved then has a pointer to a nonexistent object. Using it is UB.

Upvotes: 4

Related Questions