Lambda隐式捕获因结构化绑定声明的变量而失败

Thr*_*r57 33 c++ lambda c++17 structured-bindings visual-studio-2017

使用以下代码,我得到一个编译错误C2065 'a': undeclared identifier(使用Visual Studio 2017):

[] {
    auto [a, b] = [] {return std::make_tuple(1, 2); }();
    auto r = [&] {return a; }(); //error C2065
}();
Run Code Online (Sandbox Code Playgroud)

但是,以下代码编译:

[] {
    int a, b;
    std::tie(a, b) = [] {return std::make_tuple(1, 2); }();
    auto r = [&] {return a; }();
}();
Run Code Online (Sandbox Code Playgroud)

我以为这两个样本是等价的.它是编译器错误还是我错过了什么?

T.C*_*.C. 43

核心问题2313改变了标准,因此结构化绑定永远不是变量的名称,使它们永远不可捕获.

P0588R1对lambda捕获措辞的重新制定使这一禁令明确:

如果lambda-expression [...]捕获结构化绑定(显式或隐式),则程序格式错误.

请注意,这个措辞应该是一个占位符,而委员会确切地指出这些捕获应该如何工作.

以前的答案保留了历史原因:


这在技术上应该编译,但这里的标准有一个错误.

标准说lambdas只能捕获变量.并且它表示非​​类似元组的结构化绑定声明不会引入变量.它引入了名称,但这些名称不是变量的名称.

另一方面,类似元组的结构化绑定声明确实引入了变量.abin auto [a, b] = std::make_tuple(1, 2);是实际的引用类型变量.所以他们可以被lambda捕获.

显然这不是一个理智的事情,委员会知道这一点,所以应该即将出现修复(虽然对于如何捕获结构化绑定应该如何工作似乎存在一些分歧).

  • C++20 有什么变化吗?GCC 现在允许您从结构化绑定中捕获变量,而 clang 则不允许。哪个编译器是正确的? (5认同)
  • *另一方面,类似元组的结构化绑定声明确实引入了变量。* Clang 12 仍然不接受它:https://gcc.godbolt.org/z/9zc8o3scT (5认同)
  • 那么就我而言,推荐的解决方法是什么? (4认同)
  • Calng 15 还是不行。叮当很奇怪。 (4认同)
  • 管道中有一篇文章可以改变这一点,但是工作文件还没有发生任何变化,所以没有什么可以更新的. (3认同)
  • 多年过去了,Clang 14 仍然不支持这一点。 (3认同)
  • 友情提示,此问题已解决。目前,类似元组的结构化绑定引入了具有唯一名称的变量,`a` 和 `b` 命名这些变量所引用的泛左值。 (2认同)
  • @PasserBy 这个修复是在当前编译器中实现的吗? (2认同)
  • @BenjaminBuch 您根本不清楚“修复”是什么意思。那是 TC 已经引用的同一篇论文,作为*禁止*将结构化绑定捕获到 lambdas 中 - 这对我来说似乎非常不幸 - 至少在委员会决定使用非占位符措辞来取代这种行为之前。你是说不同的论文吗?如果是这样,我会很感激它的正确编号。 (2认同)
  • 每句话我:WTF (2认同)

小智 26

现在,自 c++20 起,lambda 可以捕获结构化绑定,请参阅

  • Clang 15 还是不行。 (4认同)
  • 很好,谢谢,如果您有机会链接标准中的相关部分那就太好了。 (2认同)
  • 即使使用 `-std=c++20` 编译,Clang 14 仍然不支持它。GCC 是最好的,Clang... (2认同)

aka*_*n64 11

可能的解决方法是使用初始化程序的lambda捕获.以下代码在Visual Studio 2017 15.5中编译良好.

[] {
    auto[a, b] = [] {return std::make_tuple(1, 2); }();
    auto r = [a = a] {return a; }();
}();
Run Code Online (Sandbox Code Playgroud)

  • 心神不宁。这就像作为新捕获列表的 python lambda 默认参数。注意:你的东西也适用于 clang(这是整个问题的唯一吠叫编译器)https://godbolt.org/z/PcAZNG (4认同)
  • 我偶尔使用的另一个(更详细的)选项是捕获指针,以避免潜在的复制。在上面的例子中: `auto r = [a = &a] { return *a; }();` (4认同)
  • 尽管请注意,这更像是“按值捕获”。 (2认同)

fde*_*hin 9

您可以像这样使用 init-capture ,如https://burnicki.pl/en/2021/04/19/capture-structured-bindings.html中的建议。该变量是通过引用捕获的,因此没有开销,也不需要处理指针。

auto [a, b] = [] { return std::make_tuple(1, 2); }();
auto r = [&a = a] { return a; }();
Run Code Online (Sandbox Code Playgroud)

对结构化绑定及其引用使用相同的名称可能会产生误导,但实际上这意味着

auto r = [&a_ref = a] { return a_ref; }();
Run Code Online (Sandbox Code Playgroud)

据我所知,它适用于从 C++17 及更高版本开始的所有编译器,包括 clang。