c++20 函数尾随 return 类型和 noexcept 运算符是否允许 lambda 捕获?

is lambda capture allowed in c++20 function trailing return type and noexcept operator?

下面的简单代码或 godbolt 与 gcc、clang 和 Visual Studio 的结果不同。

auto foo1(int n) -> decltype(([n]() { return n+1; }()))
// gcc error: use of parameter outside function body before '+' token
{
    return [n]() { return n+1; }();
}

auto foo2(int n) -> decltype(([&n]() { return n+1; }()))
// gcc error: use of parameter outside function body before '+' token
{
    return [&n]() { return n+1; }();
}

auto foo3(int n) -> decltype(([&]() { return n+1; }()))
// gcc error: use of parameter outside function body before '+' token
// gcc and clang error: non-local lambda expression cannot have a capture-default
// VS2022 17.1 warning C5253: a non-local lambda cannot have a capture default
{
    return [&]() { return n+1; }();
}

如果 lambda 在函数尾部 return 类型(或 noexcept(noexcept(...)))中,gcc 似乎不允许任何类型的捕获。 clang 可以按值捕获和按引用捕获,但不能使用默认捕获。 Visual Studio 最高 17.0 允许任何类型的捕获。 Visual Studio 17.1 给出捕获默认警告。

此处正确的行为是什么?似乎 gcc、clang 和最新的 VS 17.1 都同意在尾随 return 类型(或 noexcept(noexcept(...)))中不允许捕获默认值。但这是一个正确的设计吗?如果函数是单行的,那么将相同的表达式放在 return 语句中非常方便,noexcept 和尾部 return 类型(通常通过宏)。但是现在如果这个表达式中有一个带有捕获(默认?)的 lambda 是不可能的。

来自[expr.prim.lambda.capture]/3

A lambda-expression shall not have a capture-default or simple-capture in its lambda-introducer unless its innermost enclosing scope is a block scope ([basic.scope.block]) or it appears within a default member initializer and its innermost enclosing scope is the corresponding class scope ([basic.scope.class]).

这意味着 [n][&n][&] 之类的捕获不允许出现在尾随 return 类型或 noexcept 说明符中,但初始化捕获例如 [i = 1] 是。

所以 GCC 拒绝前两个函数定义是正确的,GCC 和 Clang 拒绝最后一个是正确的。