在 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
【往期回顾】