MSVC对C++20中闭包类型的默认构造函数的行为有所不同
该标准说
如果 lambda 表达式具有 lambda 捕获和默认的默认构造函数,则与 lambda 表达式关联的闭包类型没有默认构造函数。如果 lambda 表达式具有 lambda 捕获和默认的复制和移动赋值运算符,则它具有删除的复制赋值运算符。它有一个默认的复制构造函数和一个默认的移动构造函数 (15.8)。[ 注意:这些特殊成员函数像往常一样被隐式定义,因此可能被定义为删除。— 尾注 ]
Cppreference特别指出(强调我的)
如果未指定捕获,则闭包类型具有默认的默认构造函数。否则,它没有默认构造函数(这包括存在捕获默认值的情况,即使它实际上没有捕获任何内容)。
如果未指定捕获,则闭包类型具有默认的复制赋值运算符和默认的移动赋值运算符。否则,它具有已删除的复制赋值运算符(这包括存在捕获默认值的情况,即使它实际上并未捕获任何内容)。
所以以下必须是有效的。
auto lambda = [&](){};
static_assert(!std::is_default_constructible<decltype(lambda)>::value);
static_assert(!std::is_assignable<decltype(lambda), decltype(lambda)>::value);
但是 MSVC 说他们是 default_constructible,等等。
https://godbolt.org/z/E6EW3rMcE
由于论文中没有具体提到capture-default,但实际上没有提到capture,我想知道这是MSVC的缺陷还是允许实现定义的。
更新
我已向 Microsoft 报告了此错误,它将在即将发布的发布链接上修复。
回答
这里的标准很明确。在[expr.prim.lambda.closure]/13 中:
与相关联的闭合类型λ-表达没有默认的构造,如果λ-表达具有λ-捕获和否则一个默认的默认构造函数。
此规则基于 lambda 的词法构成,而不是我们为确定是否捕获到任何内容而进行的语义分析。这是语法上的区别。
如果 lambda 以 开头[],则它没有lambda-capture,因此具有默认的默认构造函数。
如果 lambda 以 开头[&],则它具有lambda-capture,因此没有默认构造函数 -无论是否捕获任何内容。是否捕获任何东西都没有关系。
cppreference 在此处添加的说明是正确且有帮助的。拉姆达[&](){}是不缺省构造(或者,由相同的逻辑,可指定的)。所以,是的,这是一个 MSVC 错误。
请注意,这与我们必须确定是否可以将 lambda 转换为函数指针的规则相同:是否有lambda-capture,而不是是否有任何捕获。因此,[](){}可以转换为void(*)()但[&](){}不是。