当前位置: 代码迷 >> 综合 >> Lambda 函数与表达式
  详细解决方案

Lambda 函数与表达式

热度:47   发布时间:2023-09-07 08:11:32.0

Lambda 函数与表达式

C++11 提供了对匿名函数的支持,称为 Lambda 函数(也叫 Lambda 表达式)。

Lambda 表达式把函数看作对象。Lambda 表达式可以像对象一样使用,比如可以将它们赋给变量和作为参数传递,还可以像函数一样对其求值。

Lambda 表达式本质上与函数声明非常类似。Lambda 表达式具体形式如下:

声明Lambda表达式

Lambda表达式完整的声明格式如下:

[capture list] (params list) mutable exception-> return type { function body }

capture list:捕获外部变量列表
params list:形参列表
mutable指示符:用来说用是否可以修改捕获的变量
exception:异常设定
return type:返回类型
function body:函数体

[capture](parameters)->return-type{body}

例如:

[](int x, int y){ return x < y ; }

如果没有返回值可以表示为:

[capture](parameters){body}

例如:

[]{ ++global_x; } 

在一个更为复杂的例子中,返回类型可以被明确的指定如下:

[](int x, int y) -> int { int z = x + y; return z + x; }

本例中,一个临时的参数 z 被创建用来存储中间结果。如同一般的函数,z 的值不会保留到下一次该不具名函数再次被调用时。

如果 lambda 函数没有传回值(例如 void),其返回类型可被完全忽略。

在Lambda表达式内可以访问当前作用域的变量,这是Lambda表达式的闭包(Closure)行为。 与JavaScript闭包不同,C++变量传递有传值和传引用的区别。可以通过前面的[]来指定:

[]      // 沒有定义任何变量。使用未定义变量会引发错误。
[x, &y] // x以传值方式传入(默认),y以引用方式传入。
[&]     // 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=]     // 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x]  // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。

auto是c++程序设计语言的关键字。用于两种情况

(1)声明变量时根据初始化表达式自动推断该变量的类型

(2)声明函数时函数返回值的占位符

auto关键字更适用于类型冗长复杂、变量使用范围专一时,使程序更清晰易读。如:

 std::vector<int> vect; for(auto it = vect.begin(); it != vect.end(); ++it){  //it的类型是std::vector<int>::iteratorstd::cin >> *it;}

或者保存lambda表达式类型的变量声明:

 auto ptr = [](double x){return x*x;};//类型为std::function<double(double)>函数对象

四、优势

(1)拥有初始化表达式的复杂类型变量声明时简化代码。

比如:

#include <string> 
#include <vector> 
void loopover(std::vector<std::string>&vs)  
{  std::vector<std::string>::iterator i=vs.begin();  for(;i<vs.end();i++)  {  }  }

变为:

#include <string> 
#include <vector> 
void loopover(std::vector<std::string>&vs)  
{  for(  auto i=vs.begin();;i<vs.end();i++)  {  }  }  

使用std::vectorstd::string::iterator来定义i是C++常用的良好的习惯,但是这样长的声明带来了代码可读性的困难,因此引入auto,使代码可读性增加。并且使用STL将会变得更加容易

(2)可以避免类型声明时的麻烦而且避免类型声明时的错误。

但是auto不能解决所有的精度问题。比如:

#include <iostream> 
using namespace std;  
int main()  
{  unsigned int a=4294967295;//最大的unsigned int值 unsigned int b=1auto c=a+b;  cout<<"a="<<a<<endl;  cout<<"b="<<b<<endl;  cout<<"c="<<c<<endl;  
}  

上面代码中,程序员希望通过声明变量c为auto就能解决a+b溢出的问题。而实际上由于a+b返回的依然是unsigned int的值,姑且c的类型依然被推导为unsigned int,auto并不能帮上忙。这个跟动态类型语言中数据hi自动进行拓展的特性还是不一样的。

五、注意的地方

(1)可以用valatile,pointer(*),reference(&),rvalue reference(&&) 来修饰auto

   auto k = 5;  auto* pK = new auto(k);  auto** ppK = new auto(&k);  const auto n = 6;  

(2)用auto声明的变量必须初始化

(3)auto不能与其他类型组合连用

(4)函数和模板参数不能被声明为auto

(5)定义在堆上的变量,使用了auto的表达式必须被初始化

int* p = new auto(0); //fine
int* pp = new auto(); // should be initialized
auto x = new auto(); // Hmmm … no intializer
auto* y = new auto(9); // Fine. Here y is a int*
auto z = new auto(9); //Fine. Here z is a int* (It is not just an int)

(6)以为auto是一个占位符,并不是一个他自己的类型,因此不能用于类型转换或其他一些操作,如sizeof和typeid

(7)定义在一个auto序列的变量必须始终推导成同一类型

auto x1 = 5, x2 = 5.0, x3='r';  /<span style="font-family: Arial, Helvetica, sans-serif;">/错误,必须是初始化为同一类型</span>

(8)auto不能自动推导成CV-qualifiers (constant & volatile qualifiers)

(9)auto会退化成指向数组的指针,除非被声明为引用

1、值捕获

值捕获和参数传递中的值传递类似,被捕获的变量的值在Lambda表达式创建时通过值拷贝的方式传入,因此随后对该变量的修改不会影响影响Lambda表达式中的值。

int main()
{int a = 123;auto f = [a] { cout << a << endl; }; a = 321;f(); // 输出:123
}

这里需要注意的是,如果以传值方式捕获外部变量,则在Lambda表达式函数体中不能修改该外部变量的值。

2、引用捕获

使用引用捕获一个外部变量,只需要在捕获列表变量前面加上一个引用说明符&。如下:

int main()
{int a = 123;auto f = [&a] { cout << a << endl; }; a = 321;f(); // 输出:321
}

从示例中可以看出,引用捕获的变量使用的实际上就是该引用所绑定的对象。

3、隐式捕获

上面的值捕获和引用捕获都需要我们在捕获列表中显示列出Lambda表达式中使用的外部变量。除此之外,我们还可以让编译器根据函数体中的代码来推断需要捕获哪些变量,这种方式称之为隐式捕获。隐式捕获有两种方式,分别是[=]和[&]。[=]表示以值捕获的方式捕获外部变量,[&]表示以引用捕获的方式捕获外部变量。

隐式值捕获示例:

int main()
{int a = 123;auto f = [=] { cout << a << endl; };    // 值捕获f(); // 输出:123
}

隐式引用捕获示例:

int main()
{int a = 123;auto f = [&] { cout << a << endl; };    // 引用捕获a = 321;f(); // 输出:321
}
  相关解决方案