初始化期间的 lambda 捕获应该是一个错误

Posted

技术标签:

【中文标题】初始化期间的 lambda 捕获应该是一个错误【英文标题】:lambda capture during initialization should be an error 【发布时间】:2017-08-20 12:29:13 【问题描述】:

我正在尝试做的是在构造一个可能无效的对象时吃异常。它非常适合使用std::optional,但我不相信std::optional 的省略会改变我看到的错误:对象在初始化之前被捕获和使用。我不认为应该首先捕获它,因为据我所知,我们还没有达到 序列点(lambda 初始化算作序列点吗?)。此外,该错误是 IMO 很容易捕获的人为错误(甚至会被捕获……视情况而定)。

lambda 如何(更重要的是,为什么)能够捕获和使用尚未初始化的foo

https://godbolt.org/g/IwcHrV

#include <string>
using namespace std;

void foo() 
  string foo = [&]()->string
    // using foo before it's been initialized == undefined behavior
    auto guessed_foo = to_string(1234);
    if ( begin(foo) == end(foo) ) 
      return guessed_foo;
    
    return ;
  ();

编译器退出,结果代码为 0

但是...用auto foo 替换string foo 的声明确实 似乎会导致类似于我希望看到的错误。

https://godbolt.org/g/GfE4WH

#include <string>
using namespace std;

void foo() 
  auto foo = [&]()->string
    auto guessed_foo = to_string(1234);
    if ( begin(foo) == end(foo) ) 
      return guessed_foo;
    
    return ;
  ();

错误:用 'auto' 类型声明的变量 'foo' 不能出现在它自己的初始化程序中

请注意,我在 Ubuntu 16.04 LTS 上使用 GCC 6.2 发现了这一点。 Godbolt 中的配置使用的是 clang 3.9.1。两者都是为 c++14 配置的。

所以我的问题是:

为什么用于初始化非自动声明变量的 lambda 捕获能够捕获和使用(尚未初始化的)变量? 为什么auto 会(在我看来是正确的)被捕获并出错? 而且,为什么以上两者有区别?听起来像是编译器错误,但是……标准中是否有特定内容声明这是正确的行为? 这可以作为参数 for auto 关键字?

【问题讨论】:

高度相关:***.com/questions/11186261/why-is-int-i-i-legal 【参考方案1】:

第二个sn-p跑到[dcl.spec.auto]/10:

如果需要具有未推断占位符类型的实体类型 要确定表达式的类型,程序是非良构的。

需要 foo 的类型来确定 lambda 主体中表达式 foo 的类型,但此时您还没有推断出 foo 的类型,因此程序是非良构的.


至于为什么允许在初始化之前捕获某些内容,请参阅Why is 'int i = i;' legal?。我们有很多使用 std::function 的递归 lambda 示例:

std::function<void(int)> foo = [&foo](int i) return foo(i - 1); ;

【讨论】:

好的,那么为什么-Winit-self -Wuninitialized 会发出int i = i; 的诊断信息,但 not 来自 lambda 表达式? godbolt.org/g/qC2iao @inetknght 这些是尽力而为的警告,不能保证。

以上是关于初始化期间的 lambda 捕获应该是一个错误的主要内容,如果未能解决你的问题,请参考以下文章

在嵌套 lambda 的情况下如何初始化 lambda 捕获?

Java:初始化错误的适当异常

编译器未捕获未初始化的成员。它是一个错误吗?

在 AWS Lambda 上处理未初始化或错误的 Redis 连接

C++学习:6补充

在异步 iCloud 初始化期间未能完成商店设置