Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda: Why are captured-by-value values const, but capture-by-reference values not?

Why are captured-by-value values const, but captured-by-reference objects not:

int a;

auto compile_error = [=]()
{
  a = 1;
}
auto compiles_ok = [&]()
{
  a = 1;
}

To me this seem illogical but it seem to be the standard? Especially as the unwanted modification of a captured value may be an annoying bug, but chances are high that the consequences are limited to lambda scope, whereas unwanted modification of objects captured by reference will often lead to more serious effects.

So why not capture by const reference per default? Or at least support [const &] and [&]? What are the reasons for this design?

As workaround you are probably supposed to use std::cref wrapped const references captured by value?

like image 668
valoh Avatar asked May 26 '13 22:05

valoh


People also ask

Why is lambda capture const?

By default, variables are captured by const value . This means when the lambda is created, the lambda captures a constant copy of the outer scope variable, which means that the lambda is not allowed to modify them. In the following example, we capture the variable ammo and try to decrement it.

Are lambda captures Const?

If not provided, the objects captured by copy are const in the lambda body. The following specifiers are allowed at most once in each sequence: mutable : allows body to modify the objects captured by copy, and to call their non-const member functions.

Where are lambda captures stored?

Lambda expression with value capture. Lambda expressions can capture of outer scope variables into a lambda function. The captured variables are stored in the closure object created for the lambda function.

Can lambda function access local variables?

A lambda expression can capture variables like local and anonymous classes. In other words, they have the same access to local variables of the enclosing scope.


2 Answers

Let's say you are capturing a pointer by value. The pointer itself is const, but access to the object it points to is not.

int i = 0;
int* p = &i;
auto l = [=]{ ++*p; };
l();
std::cout << i << std::endl;  // outputs 1

This lambda is equivalent to:

struct lambda {
    int* p;
    lambda(int* p_) : p(p_) {}
    void operator()() const { ++*p; }
};

The const on the operator()() makes usage of p equivalent to declaring it as:

int* const p;

Similar thing happens with a reference. The reference itself is "const" (in quotes because references cannot be reseated), but access to the object it refers to is not.

like image 114
Nevin Avatar answered Sep 25 '22 02:09

Nevin


Captured references are also const. Or rather, references are always implicitly const -- there is no syntax in the language that allows you to change where a reference points to. a = 1; when a is a reference is not changing the reference, but changing the thing that the reference references.

When you talk about "const reference", I think you are confused. You are talking about "reference to const int" (const int &). The "const" there refers to the thing the reference points to, not the reference itself. It's analogous with pointers: with "pointer to const int" (const int *), the pointer itself is not const -- you can assign to a variable of this type all you want. A real "const pointer" would be int *const. Here, you cannot assign to something of this type; but you can modify the int it points to. Hence, the "const" for the pointer or reference is separate from the "const" for the thing it points to. You can also have a "const pointer to const int": const int *const.

like image 22
newacct Avatar answered Sep 25 '22 02:09

newacct