C++ – 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?

Best Solution

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; };
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.