优秀的编程知识分享平台

网站首页 > 技术文章 正文

C++20 新特性(11):lambda对this的捕捉改进

nanyue 2024-07-30 03:12:02 技术文章 5 ℃

在 C++11 标准中增加了 lambda 表达式,先简单回顾一下 lambda 中的缺省捕捉,再看 C++20 对 this 的捕捉的改进。



lambda表达式的缺省捕捉

lambda表达式中,可以显式捕捉指定的变量,也可以进行隐含的缺省捕捉,缺省捕捉有两种方式:

  • &:表示按引用的方式进行捕捉
  • =:表示按值的方式进行捕捉,具体变量的值会复制一个拷贝,这个拷贝是const不可修改的。注意,按值捕捉变量的值,是定义 lambda 时候的值,而不是执行 lambda 时候的值。

在使用了缺省捕捉的同时,也可以对指定变量进行显式捕捉,但这个时候,捕捉的方式应该是不一样的,例如缺省是按引用方式捕捉,那么可以指定某些变量时按值的方式进行捕捉,但不能再指定某些变量重复的按引用方式捕捉。

int n = 0;
int k = 0;
auto f1 = [ = ] { return n + k; };  // <1> OK,缺省按值捕捉
auto f2 = [ & ] { return ++n; };   // <2> OK,缺省按引用捕捉
auto f3 = [ =, k ] { return n + k; };  // <3> Error,缺省按值捕捉,不能再单个变量按值捕捉
auto f4 = [ =, &k ] { return n + k; };  // <4> OK,缺省按值捕捉,指定变量按引用捕捉
auto f5 = [ &, k ] { return n + k; };    // <5> OK,缺省按引用捕捉,指定变量按值捕捉
auto f6 = [ &, &k ] { return n + k; };  // <6> Error,缺省按引用捕捉,不能再单个变量按引用捕捉
auto f7 = [ k, k ] { return n + k; };  // <7> Error,同一个变量不能捕捉两次,
auto f8 = [ k, &k ] { return n + k; };  // <7> Error,同一个变量不能捕捉两次,不管是否按值还是按引用

C++20 中 lambda 对 this 的捕捉改进

在类的成员函数中定义 lambda 时,this 指针是个特殊情况。因为类的成员函数中,默认都可以通过 this 指针访问类的成员变量,因此在 C++11 中新增 lambda 的时候,要求 this 指针只能使用引用方式,并且要求使用 “ = ” 缺省按值捕捉时,其他变量按引用捕捉都应该使用 “ & ” 来表示引用,并且特别指出 [ =, this ] 的写法是不合法的。

但按引用方式访问类成员变量不一定能满足所有需求,因此在C++17中,增加了按值的方式访问类成员变量,也就是捕捉 *this 。

这样对按值还是按引用捕捉 this 带来了差异,因此在C++20中,要求对 this 的捕捉要显式说明,不在 “ = & ” 的缺省捕捉中。同时,原来的 [ =, this ] 语法是不合法的,现在也改成合法了。

#include <stdio.h>

class SA
{
public: 
    int m_x { 0 };
    
    void func();
};

void SA::func()
{
    int n = 0;
    int ret = 0;

    n = 1; m_x = 2;
    auto f1 = [ = ] ( int k ) -> int {
        // n = 10;   // <1> 按值捕捉,不能修改
        return n + k;
    };
    n = 100; m_x = 200;
    ret = f1( 3 );
    printf( "n %d, m_x %d, ret %d\n", n, m_x, ret );

    n = 1; m_x = 2;
    auto f2 = [ =, this ] ( int k ) -> int {  // <2> C++17不支持,C++20支持,访问类成员变量,要求显式捕捉 this
        // n = 10;
        m_x = 20;   // <3> this按引用捕捉,可以修改
        return n + m_x + k;
    };
    n = 100; m_x = 200;
    ret = f2( 3 );
    printf( "n %d, m_x %d, ret %d\n", n, m_x, ret );

    n = 1; m_x = 2;
    auto f3 = [ =, *this ] ( int k ) -> int {
        // n = 10; 
        // m_x = 20;  // <4> this按值捕捉,不能修改
        return n + m_x + k;
    };
    n = 100; m_x = 200;
    ret = f3( 3 );
    printf( "n %d, m_x %d, ret %d\n", n, m_x, ret );

    n = 1; m_x = 2;
    auto f4 = [ & ] ( int k ) -> int {
        n = 10;
        return n + k;
    };
    n = 100; m_x = 200;
    ret = f4( 3 );
    printf( "n %d, m_x %d, ret %d\n", n, m_x, ret );
  
      n = 1; m_x = 2;
    auto f5 = [ &, this ] ( int k ) -> int {  // <5> this按引用捕捉,其实和缺省按引用捕捉时重复的,不过标准一直都要求允许
        n = 10;
        m_x = 20;
        return n + m_x + k;
    };
    n = 100; m_x = 200;
    ret = f5( 3 );
    printf( "n %d, m_x %d, ret %d\n", n, m_x, ret );

    n = 1; m_x = 2;
    auto f6 = [ &, *this ] ( int k ) -> int {
        n = 10;
        // m_x = 20;  // <6> this 按值捕捉,不能修改
        return n + m_x + k;
    };
    n = 100; m_x = 200;
    ret = f6( 3 );
    printf( "n %d, m_x %d, ret %d\n", n, m_x, ret );

    return;
}

int main( int argc, char * argv[] )
{
    struct SA a1;
    a1.func();

    return 0;
}

编译和输出的结果为:

[smlc@test code]$ g++ -std=c++20 a11.cpp
[smlc@test code]$ ./a.out
n 100, m_x 200, ret 4
n 100, m_x 20, ret 24
n 100, m_x 200, ret 6
n 10, m_x 200, ret 13
n 10, m_x 20, ret 33
n 10, m_x 200, ret 15


【往期回顾】

C++20 新特性(10):字符串类型的改进

C++20 新特性(9):指针和数组相关的两个改进

Tags:

最近发表
标签列表