this
的初始化捕获
(void) [p = this]() mutable {
p = nullptr; //#1: ok everywhere
(void)p;
};
这里使用了init-capture来以值的方式捕获this
指针,根据[expr.prim.lambda.capture]/6的意思,它是this
指针的一个副本。在this
被const
修饰的情况下,即使lambda是可变的(与'point to const'相比),该副本自然不能用于更改this
,但是由于lambda是可变的,指针(副本)可以用于指向其他东西,例如nullptr
。
struct S {
void f() const {
(void) [p = this]() mutable {
p->value++;
p = nullptr;
(void)p;
};
}
void f() {
(void) [p = this]() mutable {
p->value++;
p = nullptr;
(void)p;
};
}
int value{};
};
this
的简单捕获:
(void) [this]() mutable {
this = nullptr;
};
根据
[expr.prim.lambda.capture],忽略
capture-default的大小写:
- capture-list包含一个capture
- capture是simple-capture或init-capture之一;我们忽略后者,因为它已经在上面讨论过了
- simple-capture有以下形式之一:
- identifier ...可选
&
identifier ...可选
this
*this
根据
[expr.prim.lambda.capture]/10 [
强调是我的]:
如果实体被隐式捕获,捕获方式为拷贝,则:
仅
simple-capture形式的
*this
允许通过拷贝显式捕获
*this
对象。然而,根据
[expr.prim.lambda.capture]/12,
simple-capture形式的
this
通过引用捕获
*this
对象:
(+) 根据[expr.prim.lambda.capture]/4,simple-capture形式的this
和*this
都表示本地实体*this
。
如果实体被显式或隐式捕获但未被拷贝捕获,则它被引用捕获。对于通过引用捕获的实体,闭包类型是否声明了其他未命名非静态数据成员是未指定的。[...]
因此:
struct S {
void f() const {
(void) [this]() mutable {
this->value++;
this = nullptr;
};
}
void f() {
(void) [this]() mutable {
this->value++;
this = nullptr;
};
}
int value{};
};
根据
[class.this]/1,
this
不是可修改的lvalue,这就是为什么
#2
无效的原因:
在非静态成员函数的函数体中,关键字
this
是一个prvalue,其值是调用该函数的对象的指针。在类型具有cv限定符序列cv并且类为
X
的成员函数中,
this
的类型为“指向cv X的指针”。 [...]
根据
[expr.prim.lambda.closure]/12,当在lambda表达式中使用
this
时也适用于此:
lambda表达式的复合语句产生函数调用运算符的函数体,但是对于名称查找、确定
this
的类型和值以及将引用非静态类成员的id表达式转换为类成员访问表达式(使用* this)[class.mfct.non-static],复合语句在lambda表达式的上下文中被认为。
MSVC接受你的片段(accepts-invalid)是错误的。
事实上,在以下示例中(
demo):
#include <iostream>
struct S;
void g(S *& ) { std::cout << "lvalue pointer to S"; }
void g(S *&& ) { std::cout << "rvalue pointer to S"; }
struct S {
void f() {
auto l = [this]() { g(this); };
l();
}
};
int main() {
S s{};
s.f();
}
我们预计第二个
g
重载函数能更好地匹配,因为
this
是一个prvalue。然而,虽然GCC和Clang的行为符合预期:
// GCC & Clang: rvalue pointer to S
MSVC 甚至无法编译该程序:
// MSVC: error, no viable overload; arg list is '(S *const )'
这违反了[class.this]/1中的规定:
[...] 在类型带有cv限定符序列cv且所属类为X的成员函数中,this
的类型应该是“指向cv X”的指针” [...]
...而不是“指向cv X的常量指针”(prvalue的constness本来就很奇怪)。
this
被复制捕获,所以更改它不会更改原始指针。这应该总是被允许的。在第二种情况下,this
被引用捕获,因为[this]
是一个特殊情况。 - VLL