简体   繁体   中英

why do we need to capture reference for a reference in lambda?

Consider this:

class TestLambda {
public:
    std::vector<char> data;
};

void test_lambda(TestLambda& obj) {
    [=]() mutable {
        obj.data.push_back(0x01);
    }();
}

int main() {
    TestLambda tst;
    tst.data.push_back(0x99);
    test_lambda(tst);

    // tst.data is unchanged at this point

    return 0;
}

After calling test_lambda what I expected was to see the change in tst.data , but this is not the case. To see the changes, I had to create lambda passing again a reference of obj , ie. [&obj]() .

Why do we need this? I mean, again a reference?

obj is already a reference. Then, lambda is capturing obj by copying it. So, obj within the lambda itself is not a reference? Why?

Could somebody explain me this? Thank you.

When used in the right hand side of an assigment, a reference works just as a "normal" variable. Whenever you define a lambda capturing by value, the lambda owns a copy of the outer variables, as if the lambda started with these lines:

auto my_inner_variable = my_outer_reference;
auto my_inner_other_variable = my_outer_other_variable;

If you want the reference to "remain" a reference, you have to capture it by reference, thus instructing the compiler to emit code like this:

auto& my_inner_variable = my_outer_reference;
auto& my_inner_other_variable = my_outer_other_variable; // if we instructed to capture everything by reference

According to the draft standard §5.1.2/p15 Lambda expressions [expr.prim.lambda] ( Emphasis Mine ):

An entity is captured by copy if it is implicitly captured and the capture-default is = or if it is explicitly captured with a capture that is not of the form & identifier or & identifier initializer. For each entity captured by copy, an unnamed non-static data member is declared in the closure type. The declaration order of these members is unspecified. The type of such a data member is the type of the corresponding captured entity if the entity is not a reference to an object, or the referenced type otherwise. [ Note: If the captured entity is a reference to a function, the corresponding data member is also a reference to a function. — end note ] A member of an anonymous union shall not be captured by copy.

Consequently, in:

void test_lambda(TestLambda& obj) {
    [=]() mutable {
        obj.data.push_back(0x01);
    }();
}

obj is captured by copy, and thus you get rightfully the described results. In other words this is the behaviour that the standard dictates for [=] capture default.

Your function test_lambda contains a nested lambda function. Inside test_lambda , the reference obj refers to tst in main. Then you call an anonymous lambda function that captures by value. Inside the lambda function obj is an copy of the obj that was inside test_lambda. Why not simply write:

void test_lambda(TestLambda& obj) {
   obj.data.push_back(0x01);
}

What you are doing now is perhaps illustrated by

void test_lambda(TestLambda& obj) {
    [=]() mutable {
        objCopy.data.push_back(0x01);
    }();
}

where objCopy was created by the value capturing lambda.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM