c++ - F.54 : If you capture this, 显式捕获所有变量(没有默认捕获)

标签 c++ cpp-core-guidelines

读完后有点困惑Cppcore Guideline F.54

关于lambda捕获

"Writing [=] in a member function appears to capture by value, but actually captures data members by reference "

例子

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

任何带有示例的方向、指南或解释都会有所帮助

Edit

After few explanation(see the answers below) created a new example to demonstrate the behaviour

#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 值

最佳答案

"Writing [=] in a member function appears to capture by value, but actually captures data members by reference "

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

一位大人物曾经说过,如果您不必很聪明,那是一件好事。这意味着你应该明确,并使用语言清楚地表达你想要发生的事情。

默认捕获捕获局部范围内的变量。 this->x 不在范围内,但 this 在范围内。因此 x 未被捕获,但 this 被捕获。但是您很可能会使用唯一的 x 来表示 this->x。

因此,尽管您订购了按值捕获,但您得到的是按引用捕获。那种东西需要你仔细阅读和集中精力,可能会让不知情的程序员措手不及。

我稍微更改了示例并添加了更多替代方案。

[=]

potemkin 捕获, promise 按值捕获一切,因此对所有类成员产生引用捕获->

[我,这个]

更明确的捕获, promise 按值捕获 i 和 this,因此减少了看到 this->x 将通过引用捕获所需的集中度。

我建议在正文中使用 this->x 而不是 x 以使其绝对清晰。

[=, my_copy_of_x = x]

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

[=,my_copy_of_x = this->x]

相同,只是更冗长了一点。明确指出 x 是成员而不是局部变量。

[my_i = i, my_copy_of_x = this->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

关于c++ - F.54 : If you capture this, 显式捕获所有变量(没有默认捕获),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45854680/

相关文章:

c++ - "Moving"指向指针的顺序容器

c++ - 为什么我们不应该对 gsl::not_null 使用指针运算?

c++ - 我可以从外部访问函数内部的静态变量吗

c++ - 错误 LMK2019 : unresolved symbol/fatal error LNK1120: 1 unresolved externals

c++ - 在头文件中包含 std::vector 会导致模板实例化错误

c++ - 如何在没有 clang-tidy 警告的情况下使用 union ?

c++ - vector 返回负大小 C++

c++ - boost::thread - 简单示例不起作用 (C++)

c++ - gsl::cstring_span 不支持 constexpr 吗?如果没有,我为什么要使用它?

c++ - 什么是 "span",我应该什么时候使用?