5

在你扔烂番茄之前

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

例子

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

标准参考

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

4

2 回答 2

5

我会说这是标准未指定的,但肯定打算不起作用。我们对 lambda 结构的了解是,来自 [expr.prim.lambda.closure]:

lambda 表达式的类型(也是闭包对象的类型)是唯一的、未命名的非联合类类型

闭包类型不是聚合类型

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

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

和:

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

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

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

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

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

于 2017-09-26T13:45:55.037 回答
1

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

它实际上可以。以下

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

于 2019-04-30T01:54:32.353 回答