首页 > 编程语言 > 详细

【C/C++】【C++11】lambda表达式

时间:2020-08-05 00:08:05      阅读:88      评论:0      收藏:0      [点我收藏+]

用法简介

一种可调用对象,定义了一个匿名函数,并且可以捕获一定范围内的变量;

auto f = [](int a)->int{return a + 1};
cout << f(1) << endl;

特点

  • 匿名函数,也可以理解为可调用的代码单元;或者理解成未命名的内联函数;
  • 有一个返回类型,一个参数列表,一个函数体;
  • 与函数不同之处:lambda表达式可以在函数内部定义,这个是常规函数做不到;

格式

  • [捕获列表](参数列表)->返回类型{函数体};
  • 这是一个返回类型后置语法(lambda表达式的返回类型后置是必须的,这个语法就这么规定);
  • 很多时候lambda表达式返回值特别明显,允许lambda表达式返回类型,省略,编译器可以自动推导;
  • lambda参数中可以有默认值;
  • 编译器如果无法推导出返回值类型,需要显示的给出返回值类型;
  • 没有参数的时候,参数列表可以省略,甚至()也能省略;
  • 捕获列表[]和函数体{}不能省略;
  • lambda调用方法和普通函数相同
  • lambda表达式可以不返回任何类型

捕获列表

通过捕获列表来捕获一定范围内的变量;范围指的什么?

  • []表示不捕获任何变量;但是不包括静态局部变量,lambda可以直接使用局部静态变量;局部静态变量是不需要捕获的;

    int i = 9;
    auto f = []{return i}; // 出错,无法捕获外部变量,不认识这个i在哪里定义;
    
  • [&]捕获外部作用域中所有变量,并作为引用在函数体内使用;

    int i = 9;
    auto f = [&]{
    	i = 5;
    	return i; //因为&的存在,允许给i赋值,从而就改变i的值
    }; 
    
  • [=]捕获外部作用域中所有变量,作为副本按值在函数中使用,也就是可以用它的值,但是不允许给它赋值;

    int i = 9;
    auto f = [=]{
    	//i = 5; //报错,非法;
        return i; 
    }
    
  • [this]一般用于类中,捕获当前类中this指针,让lambda表达式有和当前类成员函数同样的访问权限;如果[]中已经使用了&或者=,就默认使用了this;捕获this的目的就是为了让lambda使用成员函数和变量;

    class Test
    {
    public:
    	int m_i = 5;
    	void func(int x, int y)
    	{
    		auto f = [this]{
    			return m_i; //引用this存在,合法
    		};
            cout << f() << endl;
    	}
    }
    
  • [变量名]如果是多个变量名,彼此之间用逗号分隔。表示按值捕获变量名代表的变量,同时不捕获其他变量;

  • [&变量名]:按引用捕获变量名代表的变量,同时不捕获其他变量;

  • [=, &变量名]:按值捕获所有外部变量,但按引用捕获&中所指的变量,等号必须写在开头位置,这个位置表示默认捕获方式(隐式捕获方式);后续其他都是显示捕获方式;

  • [&, 变量名]:按引用来捕获所有外部变量,但是按值来捕获后面的变量;

总结:

? lambda表达式对能访问的外部变量控制非常细致

lambda表达式延迟调用易出错细节分析

int x = 5;
auto f = [=]{ //捕获时刻,x的值就已经赋值到表达式中了; 
	return x;
}
x = 10;
cout << f() << endl; //实际是5;


int x = 5;
auto f = [&]{ 
	return x;
}
x = 10;
cout << f() << endl; //x = 10;

lambda表达式中的mutable

//mutable 易变的
int x = 5;
auto f = [=]() mutable //要加mutable,()不能省略;
{
    x = 6;
    return x;
}

lambda表达式的类型以及存储

C++11中lambda表达式的类型被称为闭包类型;

闭包:函数内的函数(可调用对象);本质上就是lambda表达式创建的运行时期的对象;

lambda表达式是一种比较特殊的,匿名的,类类型的对象(也就是定义了一个类类型,又生成了一个匿名的该类类型的对象【闭包】)

可以认为它是一种带有operator()的类类型对象;也就是仿函数对象;

也可以用std::function和std::bind来保存和调用lambda表达式;每个lambda都会触发编译器生成一个独一无二的类类型;

std::function<int(int)> fc = [](int x) {return x;};
cout << fc(15) << endl;		

//bind第一个参数是函数指针,第二个参数是真正的函数参数
std::function<int(int)> fc_bind = std::bind(
    	[](int x) {return x;}, 16
    };
    
cout << fc(15) << endl;		
cout << fc_bind(16) << endl;

lambda这种语法,可以就地的定义匿名函数,就地封装短小的功能闭包;

不捕获任何变量的lambda表达式,也就是捕获列表为空,可以转换成一个普通的函数指针;

using func_type = int(*) (int); //定义一个函数指针类型
func_type fp = [](int x) {return x;};
cout << fp(1) << endl;

语法糖

  • 便捷写法
  • 语法糖:目的是让写的代码更简单,看起来更容易理解,减少代码出错的几率;基于语言现有特性,构建出一个东西,程序员用起来会很方便。但它没有增加语言的原有功能;
  • lambada表达式可以看成定义仿函数闭包的语法糖;

lambda表达式优点总结

for_each简介

  • 头文件 #include
  • for_each是函数模板
void myfunc(int i)
{
	cout << i << endl;
}

vector<int> my_vector = {10, 20, 30, 40, 50};
for_each(my_vector.begin(), my_vector.end(), myfunc);

int isum = 0;
for_each(my_vector.begin(), my_vector.end(), [&isum](int val){
    isum += val;
    cout <<val << " ";
});
cout << endl;
cout << isum << endl;

find_if简介

  • 函数模板
  • 查找一个东西,查找目标取决于第三个参数,可以是一个函数对象;
  • 返回一个迭代器,返回第一个满足条件的元素,如果这样的元素不存在,则这个迭代器就指向end
vector<int> my_vector = {10, 20, 30, 40, 50};
auto res = find_if(my_vector.begin(), my_vector.end(), [](int val){
	cout << val << endl;
    if(val > 15) return true; //返回true,停止遍历
    return false; //只要返回false,find_if就不停的遍历,直到遍历完
    //如果第三个参数可调用参数返回true,find_if就停止遍历;
})

总结:善用lambda表达式,让代码更简洁,提高效率

lambda表达式

捕获列表中的&

捕获外部作用域中所有变量,不包括静态变量,并作为引用在lambda表达式中使用;

按照引用这种捕获方式,会导致lambda表达式包含绑定到局部变量的引用;

引用捕获超出范围也叫做 引用悬空;

std::vector<std::function<bool(int)>> gv; //全局变量,每个元素都是个function,每个function给进去的int,返回的是bool;

void func()
{
	srand((unsigned)time(NULL));
	int tmp_value = rand() % 6;
	gv.push_back(
		[&](int tv){
			if(tv % tmp_value == 0) return true;
            return false;
	});
}

int main()
{
    func();
    //cout << gv[0](10) << endl; 非法调用,会出问题 采用按值捕获可以解决;[=]
}

形参列表可以使用auto

C++14允许在lambda表达式的形参列表中使用auto

成员变量的捕获问题

捕获:只针对在创建lambda表达式的作用域内可见的非静态局部变量(包括形参);

this指向对象本身,所以这里用[=]捕捉的是this指针值;

用来[=]或者[&]等价于用了this

std::vector<std::function<bool(int)>> gv; //全局变量,每个元素都是个function,每个function给进去的int,返回的是bool;

class AT	
{
public:
    int m_value = 7;
    void addItem()
    {
		gv.push_back([=](auto tv)){ //=相当于有this
            //=按值捕获
            //没有等号不能访问成员变量
            //
            cout << m_value << endl;
            if(tv % m_value == 0)
            {
                return true;
            }
            return false;
        } 	    
    };
    
}

int main()
{
    AT *pat = new AT();
    pat -> addItem();
    cout << gv[0](10) << endl; //lambda表达式执行正确与否,取决于pat对象是否存在;只有pat对象存在,表达式执行才正确;
    delete pat;
}

std::vector<std::function<bool(int)>> gv; //全局变量,每个元素都是个function,每个function给进去的int,返回的是bool;

class AT	
{
public:
    int m_v = 7;
    void addItem()
    {
    	auto m_value = m_v;
		gv.push_back([=](auto tv)){ //=相当于有this
            //=按值捕获
            //没有等号不能访问成员变量
            //
            cout << m_value << endl;
            if(tv % m_value == 0)
            {
                return true;
            }
            return false;
        } 	    
    };
    
}

int main()
{
    AT *pat = new AT();
    pat -> addItem();
    cout << gv[0](10) << endl; //lambda表达式执行正确与否,取决于pat对象是否存在;只有pat对象存在,表达式执行才正确;
    delete pat;
}

广义lambda捕获

std::vector<std::function<bool(int)>> gv; //全局变量,每个元素都是个function,每个function给进去的int,返回的是bool;

class AT	
{
public:
    int m_value = 7;
    void addItem()
    {
		gv.push_back([abc = m_value](auto tv)){ //=相当于有this
            //=按值捕获
            //没有等号不能访问成员变量
            //
            cout << m_value << endl;
            if(tv % m_value == 0)
            {
                return true;
            }
            return false;
        } 	    
    };
    
}

int main()
{
    AT *pat = new AT();
    pat -> addItem();
    cout << gv[0](10) << endl; //lambda表达式执行正确与否,取决于pat对象是否存在;只有pat对象存在,表达式执行才正确;
    delete pat;
}

静态局部变量

捕获是不包括静态局部变量的,也就是说静态局部变量是不能被捕获的;但是可以再lambda中使用,另外,静态局部变量是保存在静态存储区,它的有效期一直到程序结束;

静态局部变量的使用类似于按引用捕获;

std::vector<std::function<bool(int)>> gv; //全局变量,每个元素都是个function,每个function给进去的int,返回的是bool;

void func()
{
	srand((unsigned)time(NULL));
	static int tmp_value = rand() % 6; //静态变量是不需要捕获的;捕获不到
	gv.push_back(
		[&](int tv){
			cout << tmp_value << endl;
			if(tv % tmp_value == 0) return true;
            return false;
	});
	tmp_value++;
	cout << tmp_value << endl;
}

int main()
{
    func();
    cout << gv[0](10) << endl; //按引用捕获的效果
}

【C/C++】【C++11】lambda表达式

原文:https://www.cnblogs.com/Trevo/p/13435679.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!