Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

lambda capture by value mutable doesn't work with const &?

Tags:

c++

c++11

lambda

Consider the following:

void test( const int &value )
{
    auto testConstRefMutableCopy = [value] () mutable {
        value = 2; // compile error: Cannot assign to a variable captured by copy in a non-mutable lambda
    };

    int valueCopy = value;
    auto testCopyMutableCopy = [valueCopy] () mutable {
        valueCopy = 2; // compiles OK
    };
}

Why is the first version a compile error when I've declared the lambda as mutable and captured value by value (which I thought made a copy of it)?

Tested with clang (x86_64-apple-darwin14.3.0), which is where the error message comes from, and Visual C++ (vc120).

like image 663
rich.e Avatar asked Jul 17 '15 21:07

rich.e


2 Answers

[C++11: 5.1.2/14]: 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 does not include an &. 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. [..]

The type of value inside your lambda is const int, because it was captured by copy from a const int&.

Thus, even though the lambda's call operator function is not const (you marked the lambda mutable), the actual implicit member value is of type const int and cannot be mutated.

Frankly, this seems absurd; I would expect this rule to say that the referenced type loses constness, as it's a copy. The presence or absence of the mutable keyword on the lambda itself (and, thus, the presence or absence of the const keyword on the generated call operator function) should be the only access control here.

In C++14 you can work around this by capturing as [value=value], which uses the same rules as auto and thus drops the const. C++'s great, ain't it?

like image 52
Lightness Races in Orbit Avatar answered Nov 11 '22 21:11

Lightness Races in Orbit


mutable allows a lambda to modify copy of a non-const parameter captured by copy, but it does not allow it for const parameters.

So this code works (and outputs inside 2 outside 1):

int a = 1;
[a]() mutable {
    a = 2; // compiles OK
    cout << "inside " << a << "\n";
}();
cout << " outside " << a << "\n";

But if we omit mutable, or make a const int, the compiler gives an error.

In our case, the first lambda gives an error because value is const:

void test( const int &value )

If we make copyValue const:

const int valueCopy = value;

then the same error will occur with the second lambda.

like image 33
AlexD Avatar answered Nov 11 '22 21:11

AlexD