在lambda表达式中指定捕获变量的目的是什么?

29

我有这段代码:

int i = 0;
[&i](){i++;}();

但我可以省略i,直接使用:

[&](){i++;}();

指定&i的目的是什么?(以及类似地,=var=)。它是否影响编译时间或运行时性能?

5个回答

26

&i表示只有i作为引用被捕获,而&表示使用lambda中的所有变量都从作用域中捕获。


int i = 0, j = 10;

[&i]{ i++; j++;}(); //error: j is not captured by reference

[&]{ i++; j++;}(); //ok: both variables are captured by reference

&i方法是语言提供的一种方式,用于限制捕获变量的数量。C++允许您完全控制捕获变量,包括捕获哪些特定变量以及如何捕获(按值还是按引用)。

i=也同理:

int i = 0, j = 10;

[i]{ i++; j++;}(); //error: j is not captured (only i is captured by value)

[=]{ i++; j++;}(); //ok: both variables are captured by value

//more
[&i, j]{ i++; j++;}(); //ok: captured i by reference, j by value

希望这能有所帮助。


2
是的,我的问题不是关于它们的区别,我已经知道了。我想知道为什么提供第一个,如果第二个已经包含了所有内容。 - SwiftMango
@texasbruce 请看我的回答,了解为什么这种语法是可用的。 - Timothy Shields
只有默认捕获使用 =,普通的按值捕获只使用名称。 - Xeo
@Xeo:我不理解你的评论。你能换个说法吗? - Nawaz
你有[=i][..., =j]用于按值捕获,但这是不正确的 - 只允许/需要[i][..., j][=]仅用于指定“按值”作为捕获默认值。 - Xeo
@Xeo:哦,你说得对。我想知道为什么没有人发现这个愚蠢的错误。谢谢 :-) - Nawaz

22
有时候你可能想以不同的方式捕获不同的变量:
std::vector<int> v { 7, 8, 9, 10, 11 };
int n = 3;

//Capture v by reference and n by value:
auto getNth = [&v, n](){ return v[n]; };

//Behavior:
n = 9999;
getNth(); //returns 10 - changing n didn't affect the lambda
v[3] = 42;
getNth(); //returns 42 - changing v did affect the lambda

这就是更详细语法的原因。

18

这个特性与性能无关,而是与可读性和可维护性有关。当你使用通用的捕获语法[&]或者[=]时,它会掩盖原始方法和lambda主体之间共享的状态。如果开发人员错误地假设一个值没有被捕获,实际上却被捕获了,那么这可能会导致方法的维护问题。使用捕获子句可以更明确地表示lambda依赖的状态。


11

指定 &i 的目的是什么?(类似于=var和=)。它会影响编译时还是运行时性能?

您可以指定值是按引用还是按值进行捕获。使用 [&] 指定要通过引用捕获所有内容,但您也可以编写 lambda 表达式来:

[=, &i, &j]()

意思是按值捕获一切,除了用引用捕获ij

在您的特定情况下,这并不有用-您可以只说按引用捕获所有内容,编译器将确定您正在使用i,并通过引用捕获它-但是当您处理多个变量被捕获时,C ++允许您单独控制每个变量如何被捕获。


4
如果你列出了要捕获的变量,你就明确表达了你想要什么。如果你使用了你不想捕获的内容,编译器会报错。这可能是件好事(实现过程中出错)或者是件坏事(你只是忘了列出该变量)。
这真的取决于特定的情况。我认为,如果lambda表达式或包含作用域比较复杂,最好还是明确你要捕获什么。
还有一个额外的要点,那就是混合按值捕获和按引用捕获-至少在其中一种方式上你必须明确表达。例如:
int i, j, k;

auto f = [=, &i]() { /* code which can read i, j and k but can only modify i */ };

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接