c++ 使用捕获限制lambda中的显式对象参数

np8igboo  于 9个月前  发布在  其他
关注(0)|答案(2)|浏览(115)

在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只有捕获有这样的限制?是否有任何实现问题?

pcww981p

pcww981p1#

有几件事在起作用。
首先,lambda捕获没有名字。所以如果我们只取一个普通的C++11 lambda:

void foo(int i) {
    auto f = [=](){ return i; };
}

字符串
f这里捕获了i,但是没有成员f.i。只是在lambda的主体中命名i神奇地引用了那个捕获。也没有办法在主体中显式地命名它-在类类型中你可以写ithis->i,但是对于lambda没有后面的选项(因为任何this的使用都会引用外部捕获的this)。
这种缺乏命名lambda本身的能力是推导这个特性的动机之一。
现在,我们可以用不同的方式来写lambda:

auto g = [=](this auto self){
    return self.i; // ??
};


在这里,self实际上是lambda本身,当你调用它时,(表达式g()会将g复制到self参数中)。这必须是一个泛型lambda,因为lambda有不可命名的类型-你不能在那里写decltype(g)。但是... g仍然是一个lambda,而lambda捕获没有名字。我们不想突然添加这些名字,所以我们最终得到了这个:

auto g2 = [=](this auto self){
    return i; // ?!?
};


诚然,这很奇怪,对吧?i在这里的行为与原始f lambda中的i相同-它被转换为lambda捕获的访问。但是 * 哪个 * lambda?self
所以现在,因为在lambda中使用显式对象参数访问lambda捕获的唯一方法是隐式的,所以我们需要确保显式对象参数实际上 * 是 * 我们当前定义的lambda-否则代码有点无意义。我们通过复制到lambda中捕获i,但是我们没有lambda范围来获取i,那我们还能怎么办
因此,引用的规则:如果有一个lambda捕获和一个显式对象参数,我们需要确保显式对象参数实际上是正在进行捕获的lambda(或从它派生的类型)。
如果lambda捕获被 * 命名 *(无论是最初还是沿着在C++23中引入的,并以某种形式推导出这一点),那么这条规则就没有必要了,因为如果你想的话,你只需要写return self.i。但是这就提出了一个不同的问题:你怎么知道[=][&]实际上...捕获了什么?因此,隐式命名捕获工作的奇怪之处,即使在显式对象参数的上下文中,也可能是这个领域中最不奇怪的选择。

rfbsl7qr

rfbsl7qr2#

需要闭包类型的对象(或子对象)来访问捕获(它的成员)。

相关问题