通过 init 捕获的默认 lambda 捕获模式会是什么样的?

What would a default lambda capture mode via init capture be like?

对于 lambda,在 C++11 中,我们可以将默认捕获模式设置为 by-value/by-ref,例如[=]/[&],可选地后跟显式捕获,by-ref/by-value,用于某些变量,例如[=,&this_is_by_ref][&,this_is_by_value].

在 C++14 中,我们还可以通过移动进行显式捕获,例如[y = std::move(x)].

Effective Modern C++ 中,第 32 项第 3 段,我阅读了

The one thing you can't express with an init capture is a default capture mode, […]

作者最有可能指的是什么?

我们已经有办法通过复制或引用来捕获我们需要的所有变量。为什么我们要用 x = y 形式来表达?

也许作者只是指“默认移动捕获”?可以像 [x = std::move(x), y = std::move(y), …] 一样工作的东西,其中列出了正文中使用的所有变量?

该段落指出不可能将 init 捕获与默认捕获模式([&] 或 [=])结合使用,并且移动语义没有新的默认捕获模式。

We already have a way to capture all variables we need by copy or by reference. Why would we want to express that with the x = y form?

你的想法是正确的。将初始捕获与默认捕获相结合将具有与旧的默认捕获方式相同的行为。也许这就是它被省略的原因。然而 init-capture 可以做一些以前做不到的新事情。 (比如给新变量赋值)

Maybe the author is referring only to the a "capture by move default"? Something which would work like [x = std::move(x), y = std::move(y), …] with all variables used in the body listed?

这也是事实。

为了更好地理解 init 捕获的新可能性,我举这个小例子。假设您要用 0 到 n 的值填充一个向量。使用 lambda 执行此操作的旧方法是:

std::vector<int> vec;
int i = 0;
std::generate_n(std::back_inserter(vec), n, [i]()mutable{
    return i++;
});

现在使用 init 捕获,您可以在 lambda 捕获中对计数变量进行值初始化:

std::vector<int> vec;
std::generate_n(std::back_inserter(vec), n, [i=0]()mutable{
    return i++;
});

这在以前是不可能的,但在很多地方都很有用。

@OutOfBound 的回答已经很好了。我只会从不同的角度表达类似的想法。

Scott Meyers 在第 32 项的前几段中注意到:

  • C++11 中引入的捕获语法即使在采用该标准的那一刻也被认为不能令人满意。
  • 原因是缺乏使用移动语义(或者,例如,通过 const 引用)进行捕获的方法,除了一些笨拙、晦涩的解决方法。
  • 标准委员会本可以通过简单地扩展 C++11 语法来改进它的功能(我想,例如通过引入 [&&, &&x]),但最终选择了一种不同的、更通用的方法:他们引入了一种新语法(“C++14 语法”)。
  • Scott 然后注意到,原则上您几乎可以完全忘记旧的 C++11 语法,而只使用 C++14 语法(具有捕获初始化的语法)。
  • 您只能使用旧语法真正做的唯一一件事是定义默认捕获模式。
  • 但是,Scott 不鼓励 C++ 程序员无论如何都使用默认捕获模式(条目 31)。
  • 所以这句话的意思是:“在实践中,C++ 程序员(或 C++ 语言)并不 需要 旧语法,我希望新语法已被采用已经在 C++11 中了。

另请注意,新的捕获语法非常灵活,它不仅可以表达您使用移动语义的意图,还可以用于 const 引用(Scott 忽略了后一点)和可变 lambda。

“初始捕获”是 [z = y] 捕获方式。

唯一无法用这种捕捉方式表达的捕捉方式是默认捕捉模式。即:

[&, z=y]{ /* some code */ }

此处 & 使用的是旧式捕获语法。它表示我们有一个默认的捕获模式。 z=y 是新式的“init capture”。

没有办法用“init capture”来表示默认的捕获模式。

您使用旧式捕获语法所做的任何其他事情,您可以使用 init 捕获语法进行复制。

即:

[x=x]{ /* some code */ }

是另一种表达方式

[x]{ /* some code */ }

[&x=x]{ /* some code */ }

是另一种表达方式

[&x]{ /* some code */ }

现在还不是那么深。

但是如果你的论点是“不要使用旧的捕获语法”,你应该证明你不再需要它了。当您定义默认捕获模式时,您不需要旧的捕获语法 except