为什么不能使用结构化绑定分解 lambda 表达式的捕获列表

Why lambda expression's capture list cannot be decomposed using structured bindings

在你扔烂番茄之前

我知道lambda分解的实际应用目前有限 因为无法找到替代失败友好的检查方式 隐藏在分解变量中的 lambda 捕获数。这只是一个理论问题,因为我没有找到涵盖捕获成员变量访问修饰符的任何标准部分。

例子

int main() {
    int a;
    auto [x] = [a]{};
    static_cast<void>(a);
    static_cast<void>(x);
    return 0;
}

标准参考

关于 lambda capture 的标准部分很长,所以我可能错过了相关片段。我注意到的是,强调与捕获 are/have 相对应的非静态成员未命名。

我想说这在标准中没有明确规定,但肯定是故意不工作的。我们对 lambda 结构的了解是,从 [expr.prim.lambda.closure]:

The type of a lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type

The closure type is not an aggregate type

并且,来自 [expr.prim.lambda.capture]:

For each entity captured by copy, an unnamed non-static data member is declared in the closure type. The declaration order of these members is unspecified.

和:

It is unspecified whether additional unnamed non-static data members are declared in the closure type for entities captured by reference. If declared, such non-static data members shall be of literal type.

使用未命名成员的目的是避免在 lambda 主体之外访问它们。这些成员另外以未指定的顺序排列的结果意味着一旦您有多个副本捕获,您甚至无法知道您的结构化绑定做了什么。

int a=1, b=2;
auto [x, y] = [a, b]{}; // x==1 or x==2??

通过引用捕获的后果不一定是命名成员意味着您甚至不知道在结构化绑定声明中要列出多少标识符。

由于未指定非静态数据成员的访问,因此有可能使它们全部 public 一致,这将满足结构化绑定的情况 3。但这与 lambda 的结构方式和结构化绑定应该如何工作的意图背道而驰,所以如果有任何实现故意这样做,我会感到惊讶。 gcc,例如,explicitly patched 禁止它。

Why lambda expression's capture list cannot be decomposed using structured bindings

其实可以的。以下

template<class... Ts> struct overload : Ts... { using Ts::operator()...; };
template<class... Ts> overload(Ts...) -> overload<Ts...>;

int main()
{
    auto f = [x = 1, y = 2]() { return x + y; };
    // auto [a, b] = f; // error: cannot decompose lambda closure type 'main()::<lambda()>'

    overload o { f, };
    auto [a, b] = o;

    return b; // returns 2
}

适用于 GCC 主干 https://godbolt.org/z/15c90z