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


91

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

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

但我对我所看到的以下特性感到好奇:

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;
};

我感到困惑(并想回答)的奇怪之处是以下原因的原因:

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

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

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

6
你为什么来?从某种意义上讲,对指针的引用可能永远有用:this无法更改,它的大小不足以更快地进行引用...无论如何它实际上并不存在,因此它具有没有真正的生命周期,这意味着对它的任何引用都将按照定义悬而未决。this是prvalue,而不是lvalue。
underscore_d

Answers:


111

[&this]不起作用的原因是因为它是语法错误。中的每个逗号分隔参数lambda-introducercapture

capture:
    identifier
    & identifier
    this

您可以看到,&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)的类型和值,并使用(*this)(9.3.1)复合语句[OF]将引用非静态类成员的id表达式转换为类成员访问表达式。[lambda]是在lambda表达式的上下文中考虑的。

因此,在使用成员名称时,lambda的行为就好像是封闭的成员函数的一部分一样(在您的示例中,使用name x),因此它将this像成员函数一样生成“隐式用法” 。

如果lambda-capture包含默认的capture-capture,则&lambda-capture中的标识符不得以开头&。如果lambda-capture包含默认的capture =,则lambda-capture不应包含, this并且其包含的每个标识符都应以开头&。标识符或this不应在lambda捕获中出现多次。

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

但是[&this][=, this]格式不正确。在最后一种情况下,gcc宽容地警告您[=,this]explicit by-copy capture of ‘this’ redundant with by-copy capture default而不是错误。


3
@KonradRudolph:如果您想通过价值来捕获某些东西而通过引用来捕获其他东西呢?还是想对捕获的内容非常明确?
Xeo

2
@KonradRudolph:这是一项安全功能。您可能会意外捕获到您不想要的名称。
安德鲁·托马佐斯

8
@KonradRudolph:块级构造不会神奇地将指向它们使用的对象的指针复制到新的不可见匿名类型中,该匿名类型随后可以在封闭范围内生存-只需在表达式中使用对象名称即可。Lambda捕获更是一项危险的业务。
安德鲁·托马佐斯

5
@KonradRudolph我会说“[&]如果您正在做类似创建要传递到控制结构的块之类的操作,则可以使用”,但是如果要生成将用于不太简单目的的lambda,则可以明确捕获。 [&]如果lambda的寿命超过当前范围,这是一个可怕的想法。但是,lambda的许多用法只是将块传递给控制结构的方法,并且该块不会超过在范围内创建的块。
Yakk-Adam Nevraumont

2
@Ruslan:不,this是关键字,this不是标识符。
安德鲁·托马佐斯

6

由于标准不在“&this捕获”列表中:

N4713 8.4.5.2捕获:

lambda-capture:
    capture-default
    capture-list
    capture-default, capture-list

capture-default:
    &
    =
capture-list:
    capture...opt
    capture-list, capture...opt
capture:
    simple-capture
    init-capture
simple-capture:
    identifier
    &identifier
    this
    * this
init-capture:
    identifier initializer
    &identifier initializer
  1. 出于lambda捕获的目的,表达式可能会引用本地实体,如下所示:

    7.3 this表达式可能引用* this。

因此,标准保证this*this有效,而&this无效。同样,捕获this意味着通过引用捕获*this(它是对象的左值),而不是通过值捕获指针!this


*this通过
sp2danny
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.