Lambda: Why are captured-by-value values const, bu

2019-03-27 14:08发布

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?

3条回答
爷、活的狠高调
2楼-- · 2019-03-27 14:25

My logic says the following: lambdas are just a piece of code, with optional needed references. In the case when you need to actually copy something (which usually happens for memory management purposes, such as copying a shared_ptr), you still don't really want the lambda to have its own state. That's quite an unusual situation.

I think only the 2 following options feel "right"

  • Enclose some code using some local variables, so that you can "pass it around"
  • The same as above, only add memory management, because maybe you want to execute that piece of code asynchronously or something, and the creating scope will disappear.

But when a lambda is "mutable", i.e., it captures values which aren't const, this means that it actually supports its own state. Meaning, every time you call a lambda, it could yield a different result, which isn't based on its actual closure, but again, on its internal state, which is kind of counter-intuitive in my book, considering that lambdas originate in functional languages.

However, C++, being C++, gives you a way to bypass that limitation, by also making it a bit uglier, just to make sure you're aware of the fact you're doing something strange.

I hope this reasons with you.

查看更多
姐就是有狂的资本
3楼-- · 2019-03-27 14:27

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.

查看更多
你好瞎i
4楼-- · 2019-03-27 14:29

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.

查看更多
登录 后发表回答