在你扔烂番茄之前
我知道lambda分解的实际应用目前是有限的,因为人们无法找到替换失败友好的方法来检查隐藏在分解变量中的lambda捕获的数量。这只是一个理论问题,因为我未能找到任何涵盖捕获成员变量访问修饰符的标准部分。
例
int main() {
int a;
auto [x] = [a]{};
static_cast<void>(a);
static_cast<void>(x);
return 0;
}
标准参考
关于lambda capture的标准部分很长,所以我可能错过了相关的片段。我注意到的是,强调对应于捕获的非静态成员是/必须是未命名的。
我说这个标准没有具体规定,但肯定是行不通的。我们对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。但这非常违背lambdas的结构方式以及结构化绑定的工作方式,所以如果有任何实现有意识地这样做,我会感到惊讶。例如,gcc,explicitly patched不允许它。
为什么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工作。