8

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

int a;

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

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

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

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

4

3 回答 3

7

假设您正在按值捕获指针。指针本身是 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; }
};

使用等同constoperator()()p其声明为:

int* const p;

引用也会发生类似的事情。引用本身是“const”(在引号中,因为引用不能被重新定位),但访问它所引用的对象不是。

于 2013-05-29T16:50:14.620 回答
3

捕获的引用也是const. 或者更确切地说,引用总是隐含const的——语言中没有允许您更改引用指向的语法。a = 1;什么时候a是引用不是改变引用,而是改变引用引用的东西。

当您谈论“常量引用”时,我认为您很困惑。您正在谈论“对 const int 的引用”(const int &)。那里的“const”指的是引用指向的东西,而不是引用本身。它与指针类似:使用“指向 const int 的指针”(const int *),指针本身不是const——您可以随心所欲地分配给这种类型的变量。一个真正的“常量指针”将是int *const. 在这里,您不能分配给这种类型的东西;但你可以修改int它指向的。因此,指针或引用的“const”与它所指向的事物的“const”是分开的。您还可以有一个“指向 const int 的 const 指针”:const int *const

于 2013-05-27T07:08:41.353 回答
0

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

我认为只有以下 2 个选项感觉“正确”

  • 使用一些局部变量来封装一些代码,这样你就可以“传递它”
  • 同上,只加内存管理,因为可能你想异步执行那段代码什么的,创建作用域就会消失。

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

但是,作为 C++ 的 C++ 为您提供了一种绕过该限制的方法,它也使它变得更丑陋,只是为了确保您意识到自己正在做一些奇怪的事情。

我希望这与你有关。

于 2013-05-27T20:26:33.313 回答