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

W.F*_*.F. 4 c++ lambda language-lawyer c++17 structured-bindings

扔烂番茄之前

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

int main() {
    int a;
    auto [x] = [a]{};
    static_cast<void>(a);
    static_cast<void>(x);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

标准参考

关于lambda捕获的标准部分很长,因此我可能错过了相关的片段。我注意到的是,重点是对应于捕获的非静态成员必须/未命名。

Bar*_*rry 5

我会说这不是标准规定的,但肯定是行不通的。我们从[expr.prim.lambda.closure]中了解到lambda结构:

lambda表达式的类型(也是闭包对象的类型)是唯一的,未命名的非工会类类型

闭包类型不是聚合类型

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

对于每个通过副本捕获的实体,在闭包类型中声明一个未命名的非静态数据成员。这些成员的声明顺序未指定。

和:

对于通过引用捕获的实体,在关闭类型中是否声明了其他未命名的非静态数据成员,尚不确定。如果声明,则此类非静态数据成员应为文字类型。

具有未命名成员的目的是避免在lambda的身体之外访问它们。这些成员另外处于未指定顺序的结果意味着,一旦您获得多个副本的捕获,您甚至将无法知道结构化绑定的功能。

int a=1, b=2;
auto [x, y] = [a, b]{}; // x==1 or x==2??
Run Code Online (Sandbox Code Playgroud)

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

由于未指定对非静态数据成员的访问,因此有可能采用一致的实现将它们全部公开,这将满足结构化绑定的情况3。但是,这与lambda的结构方式以及结构化绑定应该如何工作的意图背道而驰,因此,如果任何实现都知道这样做的话,我会感到惊讶。例如,对gcc进行了明确的修补以禁止它。