Given a lambda capture, what rules determine the t

2019-08-02 19:24发布

问题:

This question already has an answer here:

  • Lambda capture reference by copy and decltype 1 answer

How can I accurately predict from a capture which type of member will be created in the lambda?

In C++, I thought that capturing an object of type T by value creates a data member of type const T, and by reference T&. But when compiling this:

#include <iostream>

struct A{
    A(){std::cout<<"A\n";}
    A(const A&){std::cout<<"A&\n";}
    void cf()const{}
    void f(){}
};


int main(){
    A a;
    A& ra=a;
    const A& cra=a;
    auto f00 = [ra, cra, &a]()-> void{
        //Fixed:
        //ra is A, cra is const A, a is A&
        //lambda is void operator()()const
        a.cf(); a.f();//pass
        //ra.cf(); ra.f();//ra.f compilation err. 
        //cra.cf(); cra.f();//cra.f compilation err
    };
    //f00(); //A&,A&

    auto f01 = [ra, cra, &a]()mutable-> void{
        //Fixed:
        //ra is A, cra is const A, a is A&
        //lambda is void operator()()mutalbe
        a.cf(); a.f();//pass
        ra.cf(); ra.f();//pass
        cra.cf(); cra.f();//cra.cf pass, but cra.f error, why?
    };
    //f01(); //A&,A&

    auto f02 = [&ra, &cra, &a]()mutable-> void{
        //Fixed:
        //ra is A&, cra is const A&, a is A&
        //lambda is void operator()()mutable
        a.cf(); a.f();//pass
        ra.cf(); ra.f();//pass
        //cra.cf(); cra.f();//cra.cf pass, but cra.f error, why?
    };
    f02(); //
    return 0;
}

I encountered the following compilation error:

test_lambda.cpp:26:25: error: passing 'const A' as 'this' argument discards qualifiers [-fpermissive]
         cra.cf(); cra.f();//pass, cra.f error
                         ^
test_lambda.cpp:8:10: note:   in call to 'void A::f()'
     void f(){}
          ^

Does this mean that cra has really been captured by reference, rather than by a copy of the referred object as I expected?

回答1:

The type of the captured entities remains the same, except that references to objects are captured as copies of the referenced objects. From CPP Reference on Lambda Closure Types:

The type of each data member is the type of the corresponding captured entity, except if the entity has reference type (in that case, references to functions are captured as lvalue references to the referenced functions, and references to objects are captured as copies of the referenced objects).

In all of your lambdas, the type of the closure member cra is A. They are not, themselves, const. However, the default function-call operator() of the lambda is. The error on line 17 about f00 is caused by the fact that you attempt to modify an closure member created by copy when calling ra.f(), but due to it having an operator() const, it can only perform const operations on its members.

This is why in all three functions calling the non-const A::f on cra gives a compilation error. You should add mutable after the lambda argument list to allow performing non-const operations on by-copy closure members.