Lambda表达式之capture子句
1、基本用法
Lambda 可在其主体中引入新的变量(用 C++14),它还可以访问(或“捕获”)周边范围内的变量。 Lambda 以 capture 子句开头。 它指定捕获哪些变量,以及捕获是通过值还是通过引用进行的。 有与号 (&) 前缀的变量通过引用进行访问,没有该前缀的变量通过值进行访问。空 capture 子句 [ ] 指示 lambda 表达式的主体不访问封闭范围中的变量,只访问lambda体中提及的变量。基本用法如下面代码所示:
int main() { using namespace std; auto p = [] {cout << "Lambda base using"; }; p(); }
2、引用传递与值传递
可以使用默认捕获模式来指示如何捕获 Lambda 体中引用的任何外部变量:[&] 表示通过引用捕获引用的所有变量,而 [=] 表示通过值捕获它们。 可以使用默认捕获模式,然后为特定变量显式指定相反的模式;使用默认捕获时,只有 Lambda 体中提及的变量才会被捕获。 例如,如果 lambda 体通过引用访问外部变量 total 并通过值访问外部变量 factor,则以下 capture 子句等效:
[&total, factor] [factor, &total] [&, factor] [=, &total] []:不捕获任何外部变量,只捕获Lambda体中的变量 [=]:以值捕获所有外部变量 [&]:以引用捕获所有外部变量 [var]:以值的方式捕获特定变量var [&var]:以引用方式捕获特定变量var [=,&var]:以值方式捕获所有外部变量,但以引用的方式捕获变量var [&,var]:以引用方式捕获所有外部变量,但以值的方式捕获变量var
基于以上内容进行代码示例如下:
1 int main() 2 { 3 using namespace std; 4 int b = 99; 5 string c = "test"; 6 7 //捕获Lambda体中的变量 8 auto p = [] {int a = 100; cout << "a value is:" << a << endl; }; 9 p(); 10 11 //以值的方式捕获所有外部变量 12 auto p1 = [=] {cout << "b value is:" << b << endl; 13 cout << "c value is:" << c.c_str() << endl; }; 14 p1(); 15 16 //以引用方式捕获所有外部变量 17 auto p2 = [&] {cout << "b before value is:" << b << endl; 18 b += 1; cout << "b after value is:" << b << endl; 19 cout << "c before value is:" << c.c_str() << endl; 20 c.append("abc"); cout << "c after value is:" << c.c_str() << endl; }; 21 p2(); 22 23 //以值的方式捕获特定变量 24 auto p3 = [b] {cout << "b value is:" << b << endl; }; 25 p3(); 26 27 //以引用方式捕获特定变量 28 auto p4 = [&b] {cout << "b before value is:" << b << endl; 29 b += 1; cout << "b after value is:" << b << endl; }; 30 p4(); 31 32 //以值方式捕获所有外部变量,但以引用的方式捕获特定变量 33 auto p5 = [=, &b] {cout << "b before value is:" << b << endl; 34 b += 1; cout << "b after value is:" << b << endl; 35 cout << "c value is:" << c.c_str() << endl; }; 36 p5(); 37 38 //以引用方式捕获所有外部变量,但以值的方式捕获特定变量 39 auto p6 = [&, b] {cout << "b value is:" << b << endl; 40 cout << "c before value is:" << c.c_str() << endl; 41 c.append("123"); cout << "c after value is:" << c.c_str() << endl; }; 42 p6(); 43 }
如果 capture 子句包含默认捕获 &,则该 capture 子句的捕获中没有任何标识符可采用 &identifier 形式。 同样,如果 capture 子句包含默认捕获 =,则该 capture 子句没有任何捕获可采用 =identifier 形式。 标识符或 this 在 capture 子句中出现的次数不能超过一次。 以下代码片段给出了一些示例:
struct S { void f(int i); }; void S::f(int i) { [&, i]{}; // OK [&, &i]{}; // ERROR: i preceded by & when & is the default [=, this]{}; // ERROR: this when = is the default [=, *this]{ }; // OK: captures this by value. See below. [i, i]{}; // ERROR: i repeated }
3、this指针在Lambda表达式中的应用
要在类成员函数体中使用 Lambda 表达式,请将 this 指针传递给 capture 子句,以提供对封闭类的成员函数和数据成员的访问权限。Visual Studio 2017 版本 15.3 及更高版本(在 /std:c++17 模式及更高版本中可用):可以通过在 capture 子句中指定 *this 通过值捕获 this 指针。 通过值捕获会将整个闭包复制到调用 Lambda 的每个调用站点。 (闭包是封装 Lambda 表达式的匿名函数对象)。当 Lambda 在并行或异步操作中执行时,通过值捕获非常有用。使用capture子句需要注意如下几点(官方):
---引用捕获可用于修改外部变量,而值捕获却不能实现此操作。 (mutable 允许修改副本,而不能修改原始项。)
1 int main() 2 { 3 using namespace std; 4 int b = 99; 5 string c = "test"; 6 7 auto p = [b]()mutable { cout << "b before value is:" << b << endl; 8 b += 1; cout << "b after value is:" << b << endl; }; 9 p(); 10 11 cout << "b current value is:" << b << endl; 12 }

---引用捕获会反映外部变量的更新,而值捕获不会。
---引用捕获引入生存期依赖项,而值捕获却没有生存期依赖项。 当 Lambda 以异步方式运行时,这一点尤其重要。 如果在异步 Lambda 中通过引用捕获局部变量,该局部变量将很容易在 Lambda 运行时消失。 代码可能会导致在运行时发生访问冲突。
this指针在Lambda表达式中的应用根据不同的C++版本对比如下:

演示代码如下:
1 struct S { 2 void test(); 3 int b; 4 }; 5 void S::test() 6 { 7 using namespace std; 8 b = 1; 9 auto p = [this] {cout << "b value is:" << b << endl; }; 10 p(); 11 12 b += 1; 13 auto p1 = [=, self = *this]{ cout << "b value is:" << self.b << endl; }; 14 p1(); 15 16 b += 2; 17 auto p2 = [=, *this]{ cout << "b value is:" << b << endl; }; 18 p2(); 19 } 20 21 int main() 22 { 23 S s; 24 s.test(); 25 }
4、capture引入并初始化新的变量
在 C++14 中,可在 Capture 子句中引入并初始化新的变量,而无需使这些变量存在于 Lambda 函数的封闭范围内。 初始化可以任何任意表达式表示;且将从该表达式生成的类型推导新变量的类型。 借助此功能,可以从周边范围捕获只移动的变量(例如 std::unique_ptr)并在 Lambda 中使用它们。
1 int main() 2 { 3 //初始化pt为12个值为0的容器 4 std::unique_ptr<vector<int>> pt = std::make_unique<vector<int>>(12); 5 6 //对12个元素重新赋值 7 for (int i = 0; i < 12; i++) 8 (*pt)[i] = i; 9 10 auto p = [ptr = move(pt)]{ 11 for (int num : *ptr) 12 cout << "current value is:" << num << endl; 13 }; 14 p(); 15 } 16 17 int main() 18 { 19 int m = 0; 20 int n = 0; 21 [&, n] (int a) mutable { m = ++n + a; }(4); 22 cout << m << endl << n << endl; 23 }

浙公网安备 33010602011771号