在C++23中,lambda表达式支持一个显式的对象参数(也就是“Deducing this”)。我发现lambda在[expr. lambda.lambda]/p5处捕获时有奇怪的限制。
给定一个lambda函数,如果函数调用操作符(可能从函数调用操作符模板示例化)的显式对象参数(如果有的话)的类型应该是:
- 闭合类型,
- 从闭包类型派生的类类型,或者
- 一个可能是cv限定的类型的引用。
[实施例2:
struct C {
template <typename T>
C(T);
};
void func(int i) {
int x = [=](this auto&&) { return i; }(); // OK
int y = [=](this C) { return i; }(); // error
int z = [](this C) { return 42; }(); // OK
}
字符串
-- end example]
问:为什么对lambda只有捕获有这样的限制?是否有任何实现问题?
2条答案
按热度按时间pcww981p1#
有几件事在起作用。
首先,lambda捕获没有名字。所以如果我们只取一个普通的C++11 lambda:
字符串
f
这里捕获了i
,但是没有成员f.i
。只是在lambda的主体中命名i
神奇地引用了那个捕获。也没有办法在主体中显式地命名它-在类类型中你可以写i
或this->i
,但是对于lambda没有后面的选项(因为任何this
的使用都会引用外部捕获的this
)。这种缺乏命名lambda本身的能力是推导这个特性的动机之一。
现在,我们可以用不同的方式来写lambda:
型
在这里,
self
实际上是lambda本身,当你调用它时,(表达式g()
会将g
复制到self
参数中)。这必须是一个泛型lambda,因为lambda有不可命名的类型-你不能在那里写decltype(g)
。但是...g
仍然是一个lambda,而lambda捕获没有名字。我们不想突然添加这些名字,所以我们最终得到了这个:型
诚然,这很奇怪,对吧?
i
在这里的行为与原始f
lambda中的i
相同-它被转换为lambda捕获的访问。但是 * 哪个 * lambda?self
。所以现在,因为在lambda中使用显式对象参数访问lambda捕获的唯一方法是隐式的,所以我们需要确保显式对象参数实际上 * 是 * 我们当前定义的lambda-否则代码有点无意义。我们通过复制到lambda中捕获
i
,但是我们没有lambda范围来获取i
,那我们还能怎么办因此,引用的规则:如果有一个lambda捕获和一个显式对象参数,我们需要确保显式对象参数实际上是正在进行捕获的lambda(或从它派生的类型)。
如果lambda捕获被 * 命名 *(无论是最初还是沿着在C++23中引入的,并以某种形式推导出这一点),那么这条规则就没有必要了,因为如果你想的话,你只需要写
return self.i
。但是这就提出了一个不同的问题:你怎么知道[=]
或[&]
实际上...捕获了什么?因此,隐式命名捕获工作的奇怪之处,即使在显式对象参数的上下文中,也可能是这个领域中最不奇怪的选择。rfbsl7qr2#
需要闭包类型的对象(或子对象)来访问捕获(它的成员)。