Lambda:为什么是通过值const捕获,而不是通过引用值捕获

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

本文关键字:引用 const 为什么 Lambda 捕获      更新时间:2023-10-16

为什么由值const捕获,而不是由引用对象捕获:

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

对我来说,这似乎不合逻辑,但这似乎是标准?特别是对捕获的值进行不必要的修改可能是一个令人讨厌的错误,但后果很可能仅限于lambda范围,而对通过引用捕获的对象进行不必要修改通常会导致更严重的影响。

那么,为什么不默认通过const引用进行捕获呢?或者至少支持[cons&]和[&'?这种设计的原因是什么?

作为解决方法,您可能应该使用由value捕获的std::cref包装的const引用?

假设您正在按值捕获指针。指针本身是常量,但对它所指向的对象的访问不是常量。

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

此lambda相当于:

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

operator()()上的const使用p相当于将其声明为:

int* const p;

引用也会发生类似的事情。引用本身是"const"(用引号括起来,因为引用无法重新密封(,但对其引用的对象的访问不是。

捕获的引用也是const。或者更确切地说,引用总是隐含的const——语言中没有语法允许您更改引用指向的位置。当a是引用时,a = 1;不是更改引用,而是更改引用引用的内容。

当你谈到"const reference"时,我想你会感到困惑。您所说的是"对const int的引用"(const int &(。那里的"const"指的是引用所指向的东西,而不是引用本身。这与指针类似:使用"pointer to const int"(const int *(,指针本身不是const——您可以随心所欲地将其分配给这种类型的变量。真正的"常量指针"应该是int *const。在这里,你不能分配给这种类型的东西;但是你可以修改它指向的int。因此,指针或引用的"const"与它指向的东西的"const"是分开的。你也可以有一个"const pointer to const int":const int *const

我的逻辑如下:lambdas只是一段代码,具有可选的必需引用。在需要实际复制某些内容的情况下(通常出于内存管理目的,例如复制shared_ptr(,您仍然不希望lambda有自己的状态。这种情况很不寻常。

我认为只有以下两个选项感觉"正确">

  • 使用一些局部变量封装一些代码,这样您就可以"传递它">
  • 和上面一样,只添加内存管理,因为也许你想异步执行这段代码,那么创建范围就会消失

但是,当lambda是"可变的"时,即它捕获的值不是常量,这意味着它实际上支持自己的状态。也就是说,每次调用lambda时,它都可能产生不同的结果,这不是基于其实际的闭包,而是基于其内部状态。考虑到lambda起源于函数式语言,这在我的书中有点违反直觉。

然而,作为C++,C++给了你一种绕过这种限制的方法,它也让它变得更丑陋,只是为了确保你意识到你在做一些奇怪的事情。

我希望这是你的理由。

相关文章: