短的例子:
#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。这应该是标准的)
在我的印象中,按值捕获的全部意义在于允许用户更改临时对象——否则我几乎总是使用按引用捕获更好,不是吗?
问题是,它是“几乎”吗?一个常见的用例似乎是返回或传递lambdas:
void registerCallback(std::function<void()> f) { /* ... */ }
void doSomething() {
std::string name = receiveName();
registerCallback([name]{ /* do something with name */ });
}
我认为mutable不是almost的例子。我认为“按值捕获”就像“允许我在捕获的实体死亡后使用它的值”,而不是“允许我更改它的副本”。但这或许是有争议的。
FWIW, c++标准化委员会的知名成员Herb Sutter在Lambda正确性和可用性问题中给出了不同的答案:
Consider this straw man example, where the programmer captures a local variable by
value and tries to modify the
captured value (which is a member variable of the lambda object):
int val = 0;
auto x = [=](item e) // look ma, [=] means explicit copy
{ use(e,++val); }; // error: count is const, need ‘mutable’
auto y = [val](item e) // darnit, I really can’t get more explicit
{ use(e,++val); }; // same error: count is const, need ‘mutable’
This feature appears to have been added out of a concern that the user
might not realize he got a copy, and in particular that since lambdas
are copyable he might be changing a different lambda’s copy.
他的论文是关于为什么在c++ 14中应该改变这一点。它很短,写得很好,如果你想知道关于这个特定的特性“委员们在想什么”,值得一读。
我也想知道为什么[=]需要显式可变的最简单的解释是在这个例子中:
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()
{
using namespace std;
int x = 5;
int y;
auto lamb = [x]() {return x + 5; };
y= lamb();
cout << y<<","<< x << endl; //outputs 10,5
x = 20;
y = lamb();
cout << y << "," << x << endl; //output 10,20
}
正如你所看到的,即使x被更改为20,仍然返回10 (x在lambda中仍然是5)
在lambda内部更改x意味着在每次调用中更改lambda本身(lambda在每次调用中都发生突变)。为了加强正确性,标准引入了mutable关键字。通过将lambda指定为mutable,就意味着对lambda的每次调用都可能导致lambda本身的更改。让我们看另一个例子:
int main()
{
using namespace std;
int x = 5;
int y;
auto lamb = [x]() mutable {return x++ + 5; };
y= lamb();
cout << y<<","<< x << endl; //outputs 10,5
x = 20;
y = lamb();
cout << y << "," << x << endl; //outputs 11,20
}
上面的例子表明,通过使lambda可变,在lambda内部改变x会在每次调用时用一个新的x值“突变”lambda,而这个x值与主函数中x的实际值没有任何关系
参见本草案,在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限定的函数不能。也许他们只是想让这些情况更直观,他们知道自己在想什么。:)