为什么我不能在 lambda 中捕获这个按引用('&this')?

2021-12-23 00:00:00 lambda c++ c++11

我理解在 lambda 中捕获 this(以修改对象属性)的正确方法如下:

auto f = [this] () {/* ... */};

但我很好奇我看到的以下特点:

class C {上市:无效的 foo() {//auto f = [] () {//这个没有被捕获auto f = [&] () {//为什么这样做?//auto f = [&this] () {//在'this'之前需要','//auto f = [this] () {//按预期工作x = 5;};F();}私人的:整数 x;};

让我感到困惑(并希望得到回答)的奇怪之处在于以下工作的原因:

auto f = [&] () {/* ... */};//通过引用捕获所有内容

以及为什么我不能通过引用明确捕获this:

auto f = [&this] () {/* ... */};//如上所示的编译器错误.

解决方案

[&this] 不起作用的原因是因为它是一个语法错误.lambda-introducer 中的每个逗号分隔参数都是一个 capture:

捕获:标识符&标识符这

您可以看到 &this 在语法上是不允许的.不允许它的原因是因为您永远不想通过引用捕获 this,因为它是一个小的 const 指针.您只想通过值传递它 - 因此该语言不支持通过引用捕获 this.

要明确捕获this,您可以使用[this] 作为lambda-introducer.

第一个 capture 可以是一个 capture-default,即:

capture-default:&=

这意味着自动捕获我使用的任何内容,分别通过引用 (&) 或通过值 (=) - 但是 this 的处理> 是特殊的 - 在这两种情况下,由于前面给出的原因,它是通过值捕获的(即使使用 & 的默认捕获,这通常意味着通过引用捕获).

5.1.2.7/8:

<块引用>

出于名称查找 (3.4) 的目的,确定 this 的类型和值 (9.3.2) 并转换 id-使用 (*this) (9.3.1) 将引用非静态类成员的表达式转换为类成员访问表达式,复合语句 [OF THE LAMBDA] 在 lambda 表达式的上下文中被考虑.

因此,当使用成员名称时,lambda 就好像它是封闭成员函数的一部分一样(例如在您的示例中使用名称 x),因此它将生成隐式用法"this 就像成员函数一样.

<块引用>

如果一个 lambda-capture 包含一个默认的 capture-default,即 &,则 lambda-capture 中的标识符不应是前面是 &.如果 lambda 捕获包含 = 的捕获默认值,则 lambda 捕获不应包含this 及其包含的每个标识符都应以 & 开头.标识符或 this 出现的次数不得超过一次在 lambda 捕获中.

所以你可以使用 [this][&][=][&,this] 作为 lambda-introducer 以按值捕获 this 指针.

然而,[&this][=, this] 是格式错误的.在最后一种情况下,gcc 宽容地警告 [=,this]显式按副本捕获'this'冗余,默认按副本捕获,而不是错误.>

I understand the correct way to capture this (to modify object properties) in a lambda is as follows:

auto f = [this] () { /* ... */ };

But I'm curious as to the following peculiarity I've seen:

class C {
    public:
        void foo() {
            // auto f = [] () { // this not captured
            auto f = [&] () { // why does this work?
            // auto f = [&this] () { // Expected ',' before 'this'
            // auto f = [this] () { // works as expected
                x = 5;
            };
            f();
        }

    private:
        int x;
};

The oddity that I am confused by (and would like answered) is why the following works:

auto f = [&] () { /* ... */ }; // capture everything by reference

And why I cannot explicitly capture this by reference:

auto f = [&this] () { /* ... */ }; // a compiler error as seen above.

解决方案

The reason [&this] doesn't work is because it is a syntax error. Each comma-seperated parameter in the lambda-introducer is a capture:

capture:
    identifier
    & identifier
    this

You can see that &this isn't allowed syntactically. The reason it isn't allowed is because you would never want to capture this by reference, as it is a small const pointer. You would only ever want to pass it by value - so the language just doesn't support capturing this by reference.

To capture this explicitly you can use [this] as the lambda-introducer.

The first capture can be a capture-default which is:

capture-default:
    &
    =

This means capture automatically whatever I use, by reference (&) or by value (=) respectively - however the treatment of this is special - in both cases it is captured by value for the reasons given previously (even with a default capture of &, which usually means capture by reference).

5.1.2.7/8:

For purposes of name lookup (3.4), determining the type and value of this (9.3.2) and transforming id- expressions referring to non-static class members into class member access expressions using (*this) (9.3.1), the compound-statement [OF THE LAMBDA] is considered in the context of the lambda-expression.

So the lambda acts as if it is part of the enclosing member function when using member names (like in your example the use of the name x), so it will generate "implicit usages" of this just like a member function does.

If a lambda-capture includes a capture-default that is &, the identifiers in the lambda-capture shall not be preceded by &. If a lambda-capture includes a capture-default that is =, the lambda-capture shall not contain this and each identifier it contains shall be preceded by &. An identifier or this shall not appear more than once in a lambda-capture.

So you can use [this], [&], [=] or [&,this] as a lambda-introducer to capture the this pointer by value.

However [&this] and [=, this] are ill-formed. In the last case gcc forgivingly warns for [=,this] that explicit by-copy capture of ‘this’ redundant with by-copy capture default rather than errors.

相关文章