短的例子:

#include <iostream>

int main()
{
    int n;
    [&](){n = 10;}();             // OK
    [=]() mutable {n = 20;}();    // OK
    // [=](){n = 10;}();          // Error: a by-value capture cannot be modified in a non-mutable lambda
    std::cout << n << "\n";       // "10"
}

问题是:为什么我们需要mutable关键字?它与传统的参数传递到命名函数有很大不同。背后的原理是什么?

在我的印象中,按值捕获的全部意义在于允许用户更改临时对象——否则我几乎总是使用按引用捕获更好,不是吗?

有什么启示吗?

(顺便说一下,我用的是MSVC2010。这应该是标准的)


当前回答

我也想知道为什么[=]需要显式可变的最简单的解释是在这个例子中:

int main()
{
    int x {1};
    auto lbd = [=]() mutable { return x += 5; };
    printf("call1:%d\n", lbd());
    printf("call2:%d\n", lbd());
    return 0;
}

输出:

call1:6
call2:11

单词:

您可以看到在第二次调用时x值是不同的(call1为1,call2为6)。

lambda对象按值保存捕获的变量(有自己的值) 复制),如果[=]。 lambda可以被调用多次。

在一般情况下,我们必须有相同的捕获变量的值,基于已知的捕获值,有相同的可预测的lambda行为,而不是在lambda工作期间更新。这就是为什么默认行为假设为const(预测lambda对象成员的变化),当用户意识到后果时,他会使用mutable来承担这个责任。

与按值捕获相同。举个例子:

auto lbd = [x]() mutable { return x += 5; };

其他回答

我也想知道为什么[=]需要显式可变的最简单的解释是在这个例子中:

int main()
{
    int x {1};
    auto lbd = [=]() mutable { return x += 5; };
    printf("call1:%d\n", lbd());
    printf("call2:%d\n", lbd());
    return 0;
}

输出:

call1:6
call2:11

单词:

您可以看到在第二次调用时x值是不同的(call1为1,call2为6)。

lambda对象按值保存捕获的变量(有自己的值) 复制),如果[=]。 lambda可以被调用多次。

在一般情况下,我们必须有相同的捕获变量的值,基于已知的捕获值,有相同的可预测的lambda行为,而不是在lambda工作期间更新。这就是为什么默认行为假设为const(预测lambda对象成员的变化),当用户意识到后果时,他会使用mutable来承担这个责任。

与按值捕获相同。举个例子:

auto lbd = [x]() mutable { return x += 5; };

参见本草案,在5.1.2下[expr.prim.],第5款:

lambda表达式的闭包类型有一个公共内联函数调用操作符(13.5.4),其参数 和返回类型由lambda表达式的参数声明子句和trailingreturn-描述 类型分别。此函数调用操作符声明为const(9.3.1)当且仅当lambdaexpression为 Parameter-declaration-clause后面不跟mutable。

编辑litb的评论: 也许他们想到了按值捕获,这样外部对变量的更改就不会反映在lambda中?引荐是双向的,这就是我的解释。但我不知道这是否有用。

编辑kizzx2的评论: 使用lambda的大多数情况下是作为算法的函子。默认的constness允许它在常量环境中使用,就像普通的const限定函数可以在那里使用,但非const限定的函数不能。也许他们只是想让这些情况更直观,他们知道自己在想什么。:)

它需要mutable,因为默认情况下,函数对象每次调用都应该产生相同的结果。这就是面向对象的函数和使用全局变量的函数之间的区别。

在我的印象中,按值捕获的全部意义在于允许用户更改临时对象——否则我几乎总是使用按引用捕获更好,不是吗?

问题是,它是“几乎”吗?一个常见的用例似乎是返回或传递lambdas:

void registerCallback(std::function<void()> f) { /* ... */ }

void doSomething() {
  std::string name = receiveName();
  registerCallback([name]{ /* do something with name */ });
}

我认为mutable不是almost的例子。我认为“按值捕获”就像“允许我在捕获的实体死亡后使用它的值”,而不是“允许我更改它的副本”。但这或许是有争议的。

我的印象是 价值捕获的关键在于 允许用户更改临时 —否则,我几乎总是更好地使用引用捕获,而不是 我吗?

N不是暂时的。N是使用lambda表达式创建的lambda-function-对象的成员。默认的期望是,调用你的lambda不会修改它的状态,因此它是const,以防止你意外地修改n。