[C/C++11语法]_[初级]_[lamba 表达式介绍]
来源:互联网 发布:mac git ssh key 生成 编辑:程序博客网 时间:2024/05/21 17:03
场景
- lambda 表达式在很多语言里都有一席之地,因为它的原因,可以在函数里快速定义一个便携的函数,或者在函数参数里直接快速构造和传递.
- 它可以说是匿名函数对象,一般只适用于某个函数内,只做临时使用.
- 一般是需要在对某个数据临时特殊处理时使用,比如对某种参数类型进行限定的再次封装和行为约束.
参考
1. C# Lambda表达式及其优势
2. Lambda Expressions in C++
3. Exception Specifications (throw) (C++)
4. noexcept (C++)
5. what-is-the-lifetime-of-a-c-lambda-expression
说明
- lambda 语法.
图1:
Capture Clause(捕抓条款)组合:
规则1:
[] : 空捕抓条款,表明 lambda body 不访问闭合范围(enclosing scope)的任何变量.
[&] : 以引用的方式访问闭合范围内的前面已声明变量.
[=] : 以值的方式访问闭合范围内的前面已声明的变量.
[this] : 访问类实例的this指针.
规则2
- &,=,this 默认类型不能同时声明
- 相同类型的捕抓不能和默认类型同时声明,比如[&,&i] // 编译错误
- 不相同类型的非默认类型可以同时声明.比如[&i,j]
- 对同一个变量不能捕抓多次或者同时以不同捕抓方式声明. [&i,&i] [&i,i]
Parameter List(参数列表)
- 和捕抓列表不一样,lambda可以输入参数,一般情况下参数是为了和 C++ 函数转换才需要.
- 也可以使用 lambda 表达式作为参数.
- 在C++14里, 如果使用的是泛型参数,那么你可以使用 auto 声明.
auto y = [] (auto first, auto second){ return first + second;};
Mutable Specification(Mutable关键字)
- 可以使用mutable来修改捕抓条款里声明的传值变量, 注意只是相当于声明了一个本地的mutable变量作为临时变量而已,并不会修改enclosing scope 变量范围的值. 看 例子1
Exception Specification(异常规范)
- 可以使用throw()来声明这个lambda 不抛出C++异常. 但是在C++11里这种使用方式已经被废弃.
Return Type(返回类型)
- vs2010 必须声明返回类型.
- gcc 可以不声明返回类型,但是body 里必须有能推导的 return 表达式类型.
其他
- 参考C++14 lambda Expression 的说明.
lambda 和 C++普通函数的转换.
- 根据C++14 lambda表达式条款6, lambda 可以转换为C++函数, 但是必须满足以下的转化条件,而且只能转换为闭包类型自带的特定类型的函数, 闭包类型自带了一个函数指针?.
The closure type for a non-generic lambda-expression with no lambda-capture has a public non-virtual non-
explicit const conversion function to pointer to function with C ++ language linkage (7.5) having the same
parameter and return types as the closure type’s function call operator.
– 转换前的 lambda 条件:
1. 非泛型.
2. 没有捕抓列表(即没有捕抓任何变量)
– 转换后的 函数
1. 同参数.
2. 相同返回类型.
3. 非虚拟
4. 非显式常量.(non-explicit const)
例子
例子1
- lambda 在STL里的使用场景.
- 由于vs2010 并不支持lambda 到 C++ 函数的转换,所以并不能通过编译.
- mutable 的作用.
vs2010
#include "stdafx.h"#include <memory>#include <Windows.h>#include <stdlib.h>#include <algorithm>#include <iostream>#include <vector>#include <string>#include <regex>class B{public: B(int value):two("B") { one = value; std::cout << "B" << std::endl; } ~B(){two.clear(); std::cout << "~B" << std::endl;} int one; std::string two;};void TestSort(){ std::cout << "TestSort" << std::endl; // 2010也不支持快速枚举. for(B* b: bs) // 创建10个对象 std::vector<B*> bs(10); int value = 0; std::generate(bs.begin(),bs.end(),[&value]()->B* { B* b = new B(++value); return b; }); // 搜索奇数的对象 std::vector<B*> bs2; std::for_each(bs.begin(),bs.end(),[&bs2](B* b) { if(b->one % 2) { bs2.push_back(b); } }); // 排序之前是升序. std::cout << "Before Sort ==" << std::endl; std::for_each(bs2.begin(),bs2.end(),[](B* b) { std::cout << b->one << std::endl; }); // 降序排列 std::cout << "After Sort ==" << std::endl; std::sort(bs2.begin(),bs2.end(),[](B* first,B* second) { return first->one > second->one; }); std::for_each(bs2.begin(),bs2.end(),[](B* b) { std::cout << b->one << std::endl; });}typedef void (*FUNC)();void Foo(FUNC func){ func();}void TestLambdaAsync(){ std::cout << "TestLambdaAsync ==" << std::endl; //2010 不支持lambda转换为FUNC,它只能用于template里的实现;需要vs2012以上才支持.vs2010支持lambda到FUNC的转换. // 这样就可以直接在 CreateThread里使用 lambda. //g++ 4.8.1 可以. // Foo([](){std::cout << "lambda" << std::endl;}); // 错误 2 error C2664: “Foo”: 不能将参数 1 从“`anonymous-namespace'::<lambda6>”转换为“FUNC”}void TestMutable(){ std::cout << "TestMutable==========" << std::endl; int m = 0; int n = 0; //去掉mutable会出现编译错误.Error:表达式必须是可以修改的左值. // mutable 作用之一就是省略掉本地变量的定义. // [&, n] (int a){ int n1 = n; m = ++n1 + a; }(4); [&, n] (int a)mutable{m = ++n + a; }(4); std::cout << m << std::endl << n << std::endl;}class Base{public: virtual ~Base() {} virtual int call( float ) =0;};template< typename T>class Eraser : public Base{public: Eraser( T t ) : m_t(t) { } int call( float f ) { return m_t(f); }private: T m_t;};class Erased{public: template<typename T> Erased( T t ) : m_erased( new Eraser<T>(t) ) { } int do_call( float f ) { return m_erased->call( f ); }private: Base* m_erased;};template<typename FUNC>class A1{public: A1(FUNC func):func_(func){} void Run() { func_(); } FUNC func_;private:};Erased* GetErased(){ int i = 9; Erased *e_useful = new Erased( [i]( float f ) mutable ->int { std::cout << ++i << std::endl; return 42; } ); return e_useful;}int main(int argc, char const *argv[]){ TestSort(); TestMutable(); int i = 0; auto func1 = [i]()mutable { std::cout << "A: " << ++i << std::endl; }; A1<decltype(func1)> a(func1); a.Run(); Erased* e_useful = GetErased(); e_useful->do_call(9); return 0;}
输出:
TestSortBBBBBBBBBBBefore Sort ==13579After Sort ==97531TestMutable==========50A: 110
例子2
- 使用了lambda 作为 pthread 的回调函数.
- 多线程下使用 shared_ptr 的方法.
gcc 4.8.1
// function_lambda_expression.cpp// compile with: /EHsc /W4#include <Windows.h>#include <algorithm>#include <iostream>#include <vector>#include <memory>#include <string>#include <string.h>#include "pthread.h"class A{public: A() { std::cout << "A" << std::endl; buf_ = (char*)malloc(6); strcpy(buf_,"hello"); } ~A() { free(buf_); buf_ = NULL; std::cout << "~A" << std::endl; } char* buf_; /* data */};// g++ 4.8.1 支持lambda函数到普通函数的转换,但是有条件,不支持capture(推理)// 查看C++14规范第6条款关于lambda表达式和普通C++函数的转换关系.// 传递共享指针,多线程共享变量例子.void TestLambdaAsync(std::shared_ptr<A>& a1){ std::cout << "Begin a1.use_count: " << a1.use_count() << std::endl; pthread_t t1; std::shared_ptr<A>* a = new std::shared_ptr<A>(a1); std::cout << "After a1.use_count: " << a1.use_count() << std::endl; // 如果是C函数指针作为参数,那么lambda也不能捕抓任何变量,如[&a],不然会报错. // error: cannot convert 'TestLambdaAsync()::__lambda0' to 'void* (*)(void*)' for argument '3' to 'int pthread_create(pthread_t*, pthread_attr_t_* const*, void* (*)(void*), void*)'},NULL); pthread_create(&t1,NULL,[](void* data)->void* { std::shared_ptr<A>* a = reinterpret_cast<std::shared_ptr<A>*>(data); std::cout << "pthread_create: " << (*a)->buf_ << std::endl; delete a; return NULL; },a);}int main(){ std::cout << "Start ==" << std::endl; std::shared_ptr<A> a(new A()); for (int i = 0; i < 10; ++i) { TestLambdaAsync(a); } while(a.use_count() > 1) { std::cout << "Sleep" << std::endl; Sleep(1); } std::cout << "Exit ==" << std::endl;}
输出:
Start ==ABegin a1.use_count: 1After a1.use_count: 2Begin a1.use_count: 2After a1.use_count: 3Begin a1.use_count: 3After a1.use_count: 4pthread_create: helloBegin a1.use_count: 3After a1.use_count: 4pthread_create: hellopthread_create: helloBegin a1.use_count: 2After a1.use_count: 3pthread_create: helloBegin a1.use_count: 3After a1.use_count: 3Begin a1.use_count: 3After a1.use_count: 4pthread_create: helloBegin a1.use_count: 3After a1.use_count: 4pthread_create: helloBegin a1.use_count: 3After a1.use_count: 4pthread_create: hellopthread_create: helloBegin a1.use_count: 2After a1.use_count: 3pthread_create: helloSleeppthread_create: helloExit ==~A
0 0
- [C/C++11语法]_[初级]_[lamba 表达式介绍]
- [C/C++11]_[初级]_[使用正则表达式库regex]
- [C/C++11]_[初级]_[使用正则表达式库进行分组查询]
- [C/C++]_[初级]_[原子操作]
- [C/C++11]_[初级]_[std::bind介绍和使用]
- [C/C++11]_[初级]_[使用enumerations类型]
- [C/C++11]_[初级]_[实用时间库chrono]
- [Object C]_[初级]_[NSArray排序]
- C语法_文件系统
- C语法_指针
- [C/C++不常见语法特性]_[初级]_[左值-右值-lvalue-rvalue]
- [C/C++]_[初级]_[C语言编译过程]
- [C/C++标准库]_[初级]_[使用正则表达式过滤Windows文件名中的非法字符]
- [Object-c]_[初级]_[数组NSArray过滤NSPredicate的简单介绍]
- [C/C++]_[初级]_[malloc-calloc-new的区别]
- [C/C++]_[初级]_[编程容易犯错的地方]
- [Object C]_[初级]_[Object C之内存管理]
- C#_初学者基础语法
- 01_linux下伪分布式环境搭建
- ios点击手势学习笔记
- 初始化数据库
- 异步加载,在编程中具体意思是什么?原理是什么?
- Eclipse背景颜色修改
- [C/C++11语法]_[初级]_[lamba 表达式介绍]
- Android音频系统之AudioTrack(一)
- eclipse笔记 快捷键
- eclipse快捷键整理
- 用Python玩数据-笔记三
- 数组
- React组件之间传值
- Android笔记——Activity生命周期与数据传递
- PHP汉字转拼音函数