F.54:如果你捕获它,显式捕获所有变量(没有默认捕获)

时间:2017-08-24 06:45:33

标签: c++ cpp-core-guidelines

阅读此Cppcore Guideline F.54后,有点困惑

关于lambda捕获

  

“在成员函数中写入[=]似乎按值捕获,但是   实际上通过引用“

捕获数据成员

实施例

class My_class {
    int x = 0;
    // ...

    void f() {
        int i = 0;
        // ...

        auto lambda = [=]{ use(i, x); };   // BAD: "looks like" copy/value capture
        // [&] has identical semantics and copies the this pointer under the current rules
        // [=,this] and [&,this] are not much better, and confusing

        x = 42;
        lambda(); // calls use(42);
        x = 43;
        lambda(); // calls use(43);

        // ...

        auto lambda2 = [i, this]{ use(i, x); }; // ok, most explicit and least confusing

        // ...
    }
};

为什么这被视为坏

auto lambda = [=]{ use(i, x); }; 

以及下面的一个好习惯

auto lambda2 = [i, this]{ use(i, x); };

我尝试了这个例子,但没有看到任何区别。可能我没有正确理解这些陈述

#include<iostream>
using namespace std;

class My_class {
public:
    int x = 0;
    // ...

    void f() {
        int i = 0;
        // ...

        auto lambda = [=]{ cout<<i<<x<<endl; };   // BAD: "looks like" copy/value capture
        // [&] has identical semantics and copies the this pointer under the current rules
        // [=,this] and [&,this] are not much better, and confusing

        x = 42;
        lambda(); // calls use(42);
        x = 43;
        lambda(); // calls use(43);

        // ...

        auto lambda2 = [i, this]{ cout<<i<<x<<endl; }; // ok, most explicit and least confusing

        lambda2();
    }
};

int main()
    {
        My_class val;
        val.f();
    }

输出

042
043
043
Program ended with exit code: 0

任何方向,指南或解释与示例都会有所帮助

  

修改

     

经过一些解释(见下面的答案)    创建了一个新的例子来演示   行为

#include<iostream>
using namespace std;

class My_class {
public:
    int x = 0;
    // ...

    void f() {
        int i = 0;
        // ...

        auto lambda = [=]{ cout<<i<<x<<endl; x=10; };   // BAD: "looks like" copy/value capture
        // [&] has identical semantics and copies the this pointer under the current rules
        // [=,this] and [&,this] are not much better, and confusing


        x = 42;
        lambda(); // calls use(42);
        cout<<"value of x is "<<x<<endl;


        x = 43;
        lambda(); // calls use(43);

        // ...

        auto lambda2 = [i, this]{ cout<<i<<x<<endl; }; // ok, most explicit and least confusing
        lambda2();
    }
};

int main()
    {
        My_class val;
        val.f();
    }

输出

042
value of x is 10
043
010
Program ended with exit code: 0

即使按值捕获,也会更改外部x值

5 个答案:

答案 0 :(得分:3)

  

“在成员函数中写入[=]似乎按值捕获,但实际上通过引用捕获数据成员”

引用说明一切,在成员函数中写入[=]似乎按值捕获,但实际上是通过引用捕获数据成员。

其中一位大牌曾经说过,如果你不必聪明,这是一件好事。意思是你应该明确,并使用该语言清楚地表达你想要发生的事情。

默认捕获捕获本地范围中的变量。 this-&gt; x不在范围内,但这是在范围内。因此,x未被捕获,但这是。但你可以使用鞋底x来表示这个 - > x。

因此,尽管您按价值订购了捕获,但您仍可通过引用获取捕获。这种事情需要你仔细阅读和集中阅读,并且可能会让不知情的程序员感到意外。

我稍微改变了一些例子并添加了一些替代品。

[=]

potemkin捕获,承诺按值捕获所有内容,通过引用为所有类成员生成捕获因为 - >&gt;

[I,这]

更明确的捕获,承诺通过值捕获i和,因此减少了看到此&gt; x将通过引用捕获所需的浓度水平。

我建议在正文中使用 this-&gt; x 而不是x来完全清楚。

[=,my_copy_of_x = x]

Init捕获语法允许通过值显式捕获x或引用所需的任何内容。 &amp; my_copy_of_x = x将产生参考捕获

[=,my_copy_of_x = this-&gt; x]

同样稍微冗长一点。清楚地说明x是成员而不是局部变量。

[my_i = i,my_copy_of_x = this-&gt; x]

这当然也适用于局部变量i

using namespace std;

class My_class {
public:
    int x = 0;

    void f() {
        int i = 0;
        x = 42;

        auto lambda1 = [=]{ cout << i << " " << x << endl; };
        auto lambda2 = [i,this]{ cout << i << " " << x <<endl; };
        auto lambda3 = [=,my_copy_of_x = x]{  cout<<i<<" "<< x << " " << my_copy_of_x << endl;  };
        auto lambda4 = [=,my_copy_of_x = this->x]{ cout<<i<<" "<< x << " " << my_copy_of_x << endl; };
        auto lambda5 = [my_i = i, my_copy_of_x = this->x]{ cout << my_i << "    " << my_copy_of_x<<endl;; };

        lambda1(); lambda2(); lambda3(); lambda4(); lambda5();
        x = 43;
        std::cout << "\nx changed\n\n";
        lambda1(); lambda2(); lambda3(); lambda4(); lambda5();
    }
};

int main()
{
    My_class val;
    val.f();
}

0 42
0 42
0 42 42
0 42 42
0    42

x changed

0 43
0 43
0 43 42
0 43 42
0    42

答案 1 :(得分:2)

  

&#34;在成员函数中写入[=]似乎按值捕获,但是   实际上通过引用&#34;

捕获数据成员

因为无法捕获数据成员。捕获的内容是this

即使this被副本捕获,您仍然可以修改所有成员this,就像通过引用捕获成员一样,是否都通过this指针访问它们是什么真的被捕了。

通过使用auto lambda2 = [i, this]{ use(i, x); };,您明确指出未捕获x,但this是。

答案 2 :(得分:1)

  

我尝试了这个例子,但没有看到任何区别。

因为在那个例子中,行为没有区别。然而,该指南是在这里,因为在可读性方面存在差异(作者认为)。

还要考虑

auto lambda3 = [i, this]{ use(i, x++); }; 

答案 3 :(得分:1)

问题在于捕获的语义在书面语法中并不明显。语法的含义取决于上下文,并且可能会使读取和维护变得混乱。

如果它不在成员函数中,可以期望auto lambda = [=]{ use(i, x); };制作x副本,这样如果x要更改,副本仍将保留其原始值。致电lambda将始终致电use(0, 42)。但它在成员中的行为并不像这样!

您编写=但是获得的行为就像您通过引用捕获一样。代码应该与它的编写方式有一个清晰明确的含义。

答案 4 :(得分:1)

问题在于

[=]{ use(i, x); }

实际上是

[=]{ use(i, this->x); }

所以我们按值捕获thisi,而我们可能错误地认为x是按值捕获的。

[i, this]{ use(i, x); };更明确。