目录
即看即用
语法:
[capture](parameters)->return-type {body}
[]叫做捕获说明符
parameters参数列表
->return-type表示返回类型,如果没有返回类型,则可以省略这部分。
我们可以这样输出"hello,world"
auto func = [] () { cout << "hello,world"; };
func(); // now call the function
变量捕获与lambda闭包实现
string name;
cin >> name;
[&](){cout << name;}();
lambda函数能够捕获lambda函数外的具有自动存储时期的变量。函数体与这些变量的集合合起来叫闭包。
[] 不截取任何变量
[&} 截取外部作用域中所有变量,并作为引用在函数体中使用
[=] 截取外部作用域中所有变量,并拷贝一份在函数体中使用
[=, &foo] 截取外部作用域中所有变量,并拷贝一份在函数体中使用,但是对foo变量使用引用
[bar] 截取bar变量并且拷贝一份在函数体重使用,同时不截取其他变量
[x, &y] x按值传递,y按引用传递
[this] 截取当前类中的this指针。如果已经使用了&或者=就默认添加此选项。
详情
lambda表达是c++中的可调用对象之一,在C++11中被引入到标准库中,使用时不需要包含任何头文件,但是编译时需要指定-std=c++11
或其他支持c++11标准的编译命令(比如-std=c++0x
或-std=c++14
或-std=c++1y
)。lambda表达式源于函数式编程的概念,简单来说它是一个匿名函数。它最大的作用就是不需要额外再写一个函数或者函数对象,避免了代码膨胀功能分散,让开发人员更加集中精力在手边的问题,提高生产效率。
基本概念和用法
c++中,普通的函数是长这样子的:
ret_value function_name(parameter) option { function_body; }
比如:
int get_value(int a) const {return a++;}
lambda表达式定义了一个匿名函数,并且可以捕获所定义的匿名函数外的变量。它的语法形式是:
[ capture ] ( parameter ) option -> return_type { body; };
其中:
- capture 捕获列表
- parameter 参数列表
- option 函数选项
- return_type 函数返回值类型
- body 函数体
比如:
// defination
auto lamb = [](int a) -> int { return a++; };
// usage
std::cout << lamb(1) << std::endl; // output: 2
组成lambda的各部分并不是都必须存在的:
- 当编译器可以推导出返回值类型的时候,可以省略返回值类型的部分
auto lamb = [](int i){return i;}; // OK, return type is int
auto lamb2 = [] () {return {1, 2};}; // Error
- lambda表达式没有参数时,参数列表可以省略
auto lamb = []{return 1;}; // OK
所以一个最简单的lambda表达式可以是下面这样,这段代码是可以通过编译的:
int main()
{
[]{}; // lambda expression
return 0;
}
捕获列表
捕获列表是lambda表达式和普通函数区别最大的地方。[]
内就是lambda表达式的捕获列表。一般来说,lambda表达式都是定义在其他函数内部,捕获列表的作用,就是使lambda表达式内部能够重用所有的外部变量。捕获列表可以有如下的形式:
[]
不捕获任何变量[&]
以引用方式捕获外部作用域的所有变量[=]
以赋值方式捕获外部作用域的所有变量[=, &foo]
以赋值方式捕获外部作用域所有变量,以引用方式捕获foo变量[bar]
以赋值方式捕获bar变量,不捕获其它变量[this]
捕获当前类的this指针,让lambda表达式拥有和当前类成员同样的访问权限,可以修改类的成员变量,使用类的成员函数。如果已经使用了&或者=,就默认添加此选项。
捕获列表示例:
#include <iostream>
class TLambda
{
public:
TLambda(): i_(0) { }
int i_;
void func(int x, int y) {
int a;
int b;
// 无法访问i_, 必须捕获this,正确写法见l4
auto l1 = [] () {
return i_;
};
// 以赋值方式捕获所有外部变量,这里捕获了this, x, y
auto l2 = [=] () {
return i_ + x + y;
};
// 以引用方式捕获所有外部变量
auto l3 = [&] () {
return i_ + x + y;
};
auto l4 = [this] () {
return i_;
};
// 以为没有捕获,所以无法访问x, y, 正确写法是l6
auto l5 = [this] () {
return i_ + x + y;
};
auto l6 = [this, x, y] () {
return i_ + x + y;
};
auto l7 = [this] () {
return ++i_;
};
// 错误,没有捕获a变量
auto l8 = [] () {
return a;
};
// a以赋值方式捕获,b以引用方式捕
auto l9 = [a, &b] () {
return a + (++b);
};
// 捕获所有外部变量,变量b以引用方式捕获,其他变量以赋值方式捕获
auto l10 = [=, &b] () {
return a + (++b);
}
}
};
int main()
{
TLambda a;
a.func(3, 4);
return 0;
}
引用和赋值,就相当于函数参数中的按值传递和引用传递。如果lambda捕获的变量在外部作用域改变了,以赋值方式捕获的变量则不会改变。按值捕获的变量在lambda表达式内部也不能被修改,如果要修改,需使用引用捕获,或者显示的指定lambda表达式为 mutable
。
// [=]
int func(int a);
// [&]
int func(int& a);
// ------------------------------------------
int a = 0;
auto f = [=] {return a;}; // 按值捕获
a += 1; // a被修改
cout << f() << endl; // output: 0
// ------------------------------------------
int a = 0;
auto f = [] {return a++;}; // Error
auto f = [] () mutable {return a++;}; // OK
lambda表达式的类型
上面一直使用auto
关键字来自动推导lambda表达式的类型,那作为强类型语言的c++,这个lambda表达式到底是什么类型呢?lambda的表达式类型在c++11中被称为『闭包类型(Closure Tyep)』,是一个特殊的、匿名的、非联合(union)、非聚合(aggregate)的类类型。可以认为它是一个带有operator()
的类,即防函数(functor)。因此可以使用std::function
和std::bind
来存储和操作lambda表达式。
std::function<int (int)> f = [](int a) {return a;};
std::function<int (int)> f = std::bind([](int a){return a;}, std::placeholders::_1);
std::cout << f(22) << std::endl;
对于没有捕获任何变量的lambda,还可以转换成一个普通的函数指针。
using func_t = int (*)(int);
func_t f = [](int a){return a;};
std::cout << f(22) << std::endl;
参考文档:
作者:lcode
链接:https://www.jianshu.com/p/923d11151027
===========================================旧文=====================================
原文;C++ 实现 RPC 网络通讯库_C++ - 蓝桥云课
lambda
的语法比较简单,下面这个 lambda
返回一个字符串。
auto lambda = []{ return "hello world"; };
lambda(); //将返回 hello world 字符串
lambda
还可以传参数。
auto lambda = [](std::string str){ return str; };
lambda("hello world"); //将返回 hello world 字符串
可以看到 lambda
就像一个函数一样,可以接受参数和返回值,因为 lambda
就是一个匿名函数。它可以方便我们写出函数式的代码。
lambda
的语法一共有如下三种。
[ captures ] ( parameters ) -> return_type { body }
[ captures ] ( parameters ) { body }
[ captures ] { body }
语法中的关键词意义如下。
- parameters:
lambda
表达式接受的参数,就像函数参数一样 - captures: 捕获变量,捕获变量的方式有两种:按引用捕获和按复制捕获
- body: 语句块
常见的 lambda
表达式的写法如下。一般情况是不需要显式写出返回类型的,代码也会更简洁。如果 lambda
表达式没有参数的时候括号也是可以省略的。
[](int i){ return i; }
[](int i) ->int { return i; }
[](){ return 0; }
[]{ return 0; }
按引用捕获需要在 [ ]
内写 &
,按复制捕获为 =
。若什么都不写则不捕获外部变量。
int a = 1;
int b = 2;
[&a](int i){ return i; } //按引用捕获变量 a
[a](int i){ return i; } //按复制捕获变量 a
[=](int i){ return i; } //按复制捕获变量 a,b
[&, b](int i){ return i; } //按引用捕获变量,除了 b(b 按复制捕获)。
接下来在 code1 目录下新建一个 code0.cpp 文件。:
#include <iostream>
int main() {
auto lambda = [] {
std::cout << "hello lambda!" << std::endl;
};
lambda();
}
编译和运行代码:在 「build」 目录下执行
g++ ../code0.cpp -o code0 -std=c++11 && ./code0
输出结果:
hello lambda!
这是最简单的一个 lambda
,接下来看其它几种形式的 lambda
,打开并编辑 code1.cpp 文件:
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
auto lambda = []{ std::cout<<"hello lambda!"<<std::endl; };
lambda();
auto lambda1 = [](int i)->int { return i; };
auto lambda2 = [](int i) { return i; };
std::cout<<lambda1(1)<<std::endl;
std::cout<<lambda2(2)<<std::endl;
std::vector<int> v{1,2,3,4,5,6,7};
std::for_each(v.begin(), v.end(), [](int i){
std::cout<<i<<" ";
});
std::cout<<std::endl;
return 0;
}
编译和运行代码:在 build 目录下执行
g++ ../code1.cpp -o code1 -std=c++11 && ./code1
输出结果:
hello lambda!
1
2
1 2 3 4 5 6 7
上面是 lambda
用于 stl 算法的经典例子。std::for_each
的时候,传入一个 lambda
,用它把 vector
容器中的每个整数打印出来,这是 lambda
的一个典型应用场景,使用 lambda
让我们的代码变得简单清晰。
接下来我们实验一下 lambda
捕获变量的情况,在 code1 目录下新建 code2_1.cpp 文件:
#include <iostream>
int main() {
int a = 1;
int b = 2;
int c = 3;
auto lambda1 = [&]{
a = 4;
b = 5;
c = 6;
};
lambda1();
std::cout<<a<<" "<<b<<" "<<c<<std::endl;
}
编译和运行代码:在 build 目录下执行
g++ ../code2_1.cpp -o code2_1 -std=c++11 && ./code2_1
输出结果:
4 5 6
可以看到我们通过引用方式捕获变量后,就可以对变量进行修改了。接下来看按拷贝方式捕获的情况,在 code1 目录下新建 code2_2.cpp 文件:
#include <iostream>
int main() {
int a = 1;
int b = 2;
int c = 3;
auto lambda1 = [&]{
a = 4;
b = 5;
c = 6;
};
lambda1();
std::cout<<a<<" "<<b<<" "<<c<<std::endl;
auto lambda2 = [a,b,c]() mutable{
a = 1;
b = 2;
c = 3;
std::cout<<"in lambda2 :"<<a<<" "<<b<<" "<<c<<std::endl;
};
lambda2();
std::cout<<a<<" "<<b<<" "<<c<<std::endl;
auto lambda3 = [=]() mutable{
a = 10;
b = 20;
c = 30;
std::cout<<"in lambda3 :"<<a<<" "<<b<<" "<<c<<std::endl;
};
lambda3();
std::cout<<a<<" "<<b<<" "<<c<<std::endl;
}
编译和运行代码:在 build 目录下执行
g++ ../code2_2.cpp -o code2_2 -std=c++11 && ./code2_2
输出结果:
4 5 6
in lambda2 :1 2 3
4 5 6
in lambda3 :10 20 30
4 5 6
可以看到 lambda2
中 a
,b
,c
按复制捕获,在 lambda
中修改 a
,b
,c
是不会修改外面的 a
,b
,c
的值。
lambda
在类中使用的时候需要捕获 this
指针才能访问类成员。
ambda
按引用捕获变量时要注意临时变量生命周期的问题,我们通过一个实验来看这个问题,在 code1 目录下新建 code3.cpp 文件,编写下面这些代码:
#include <iostream>
#include <string>
#include <functional>
std::function<void()> test(){
std::string str = "hello";
auto lambda = [&str]{
std::cout<<str<<" "<<"lambda";
};
return lambda;
}
int main() {
auto lambda1 = test();
lambda1();
}
编译和运行代码:在 build 目录下执行
g++ ../code3.cpp -o code3 -std=c++11 && ./code3
输出结果:
将不会输出字符串,因为 test
函数中的 lambda
捕获的是一个临时变量 str
的引用,当 test
函数结束时 str
被析构,这时 lambda
捕获的 str
就变成一个无效的字符串,因此在 main
中再调用 lambda1
时是不会打印出字符串的。
摘自:实验课网站《C++ 实现 RPC 网络通讯库》
c++11:lambda函数及其用法
为什么需要lambda函数
匿名函数是许多编程语言都支持的概念,有函数体,没有函数名。1958年,lisp首先采用匿名函数,匿名函数最常用的是作为回调函数的值。正因为有这样的需求,c++引入了lambda 函数,你可以在你的源码中内联一个lambda函数,这就使得创建快速的,一次性的函数变得简单了。例如,你可以把lambda函数可在参数中传递给std::sort函数
#include <algorithm>
#include <cmath>
void abssort(float* x, unsigned N) {
std::sort(x, x + N,
// Lambda expression begins
[](float a, float b) {
return std::abs(a) < std::abs(b);
});
}
你可能会问,使用函数对象不是也可以吗?是的,函数对象当然没问题,自己写的回调函数,你可以传个函数指针也没有问题。他们有优点也有缺点。函数对象能维护状态,但语法开销大,而函数指针语法开销小,却没法保存范围内的状态。如果你觉得鱼和熊掌不可兼得,那你可错了。lambda函数结合了两者的优点,让你写出优雅简洁的代码。
基本lambda语法
基本形式如下:
[capture](parameters)->return-type {body}
[]叫做捕获说明符,表示一个lambda表达式的开始。接下来是参数列表,即这个匿名的lambda函数的参数,->return-type表示返回类型,如果没有返回类型,则可以省略这部分。想知道为什么返回类型可以这么表示,这涉及到c++11的另一特性,参见自动类型推导,最后就是函数体部分了。
我们可以这样输出"hello,world"
auto func = [] () { cout << "hello,world"; };
func(); // now call the function
变量捕获与lambda闭包实现
string name;
cin >> name;
[&](){cout << name;}();
lambda函数能够捕获lambda函数外的具有自动存储时期的变量。函数体与这些变量的集合合起来叫闭包。
[] 不截取任何变量
[&} 截取外部作用域中所有变量,并作为引用在函数体中使用
[=] 截取外部作用域中所有变量,并拷贝一份在函数体中使用
[=, &foo] 截取外部作用域中所有变量,并拷贝一份在函数体中使用,但是对foo变量使用引用
[bar] 截取bar变量并且拷贝一份在函数体重使用,同时不截取其他变量
[x, &y] x按值传递,y按引用传递
[this] 截取当前类中的this指针。如果已经使用了&或者=就默认添加此选项。
看到这,不禁要问,这魔法般的变量捕获是怎么实现的呢?原来,lambda是通过创建个小类来实现的。这个类重载了操作符(),一个lambda函数是该类的一个实例。当该类被构造时,周围的变量就传递给构造函数并以成员变量保存起来。看起来跟函数对象很相似。
最后,lambda函数的类型是什么呢,答案是std:function。
---------------------
作者:booirror
来源:CSDN
原文:https://blog.csdn.net/booirror/article/details/26973611
版权声明:本文为博主原创文章,转载请附上博文链接!