this 的初始化捕获
(void) [p = this]() mutable {
p = nullptr; //#1: ok everywhere
(void)p;
};
这使用初始化捕获来捕获this 指针按值,根据[expr.prim.lambda.capture]/6,这意味着它是this 指针的副本。在this 是const 限定的上下文中,副本自然不能用于更改this(即使lambda 是可变的;与'point to const' 比较),但是as lambda 是可变的,指针(副本)可用于指向不同的东西,例如nullptr.
struct S {
void f() const {
(void) [p = this]() mutable {
p->value++; // ill-formed: 'this' is pointer to const, meaning
// 'p' is pointer to const.
p = nullptr; // OK: 'p' is not const pointer
(void)p;
};
}
void f() {
(void) [p = this]() mutable {
p->value++; // OK: 'this' is pointer to non-const, meaning
// 'p' is pointer to non-const.
p = nullptr; // OK: 'p' is not const pointer
(void)p;
};
}
int value{};
};
this 的简单捕获:
(void) [this]() mutable {
this = nullptr; //#2: ok in MSVC only
};
按照[expr.prim.lambda.capture],忽略capture-default的情况:s:
- 一个 capture-list 包含一个 capture
-
capture 是 simple-capture 或 init-capture;我们忽略后一种情况,因为它已经在上面介绍过
-
simply-capture 具有以下形式之一:
-
标识符 ...选择
-
&标识符 ...opt
this
*this
根据[expr.prim.lambda.capture]/10 [强调我的]:
一个实体被副本捕获如果
只有 simple-capture 形式 *this 允许通过复制显式捕获 *this 对象。然而,简单的捕获this 通过引用捕获*this 对象(+),根据[expr.prim.lambda.capture]/12:
(+) simple-capture:s this 和 *this 都表示本地实体 *this,根据 [expr.prim.lambda.capture]/4。
一个实体被引用捕获,如果它是隐式或
明确捕获但未通过副本捕获。未指定是否在
通过引用捕获的实体的闭包类型。 [...]
因此:
struct S {
void f() const {
(void) [this]() mutable {
// '*this' explicitly-captured by-reference
this->value++; // ill-formed: 'this' is pointer to const
this = nullptr; // #2 ill-formed: 'this' is not a modifyable lvalue
};
}
void f() {
(void) [this]() mutable {
// '*this' explicitly-captured by-reference
this->value++; // OK: 'this' is pointer to non-const
this = nullptr; // #2 ill-formed: 'this' is not a modifyable lvalue
};
}
int value{};
};
根据[class.this]/1,this 不是可修改的左值,这就是为什么#2 格式错误:
在非静态 ([class.mfct]) 成员函数的主体中,关键字this 是纯右值,其值是指向调用该函数的对象的指针.类型为 cv-qualifier-seq cv 且类为 X 的成员函数中 this 的类型是“指向 cv X 的指针”。 [...]
根据 [expr.prim.lambda.closure]/12,这也适用于 this 在 lambdas 中使用的情况:
lambda 表达式的复合语句产生函数调用运算符的函数体 ([dcl.fct.def]),但 出于名称查找的目的,确定类型 和 this 的值 并使用 (*this) ([class.mfct.non-static]) 将引用非静态类成员的 id 表达式转换为类成员访问表达式, 复合语句在 lambda 表达式的上下文中被考虑。
MSVC 接受你的 sn-p 是不正确的(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-qualifier-seq cv 且类为X 的成员函数中this 的类型是“指向cv X 的指针” [ ...]
... 而不是“指向 cv X 的常量指针”(prvalue 上的常量会很奇怪,首先)。