C++11(lambad表达式)
更多教程笔记请查看我的上一篇文章:点击跳转
C++11(lambad表达式)自学之旅开始!
本次将介绍C++11里新的函数写法:labmda表达式
lambda表达式很早就有了,后来被C++11引入,原本我对lambda的语法一点兴趣都没有,可是之前还在公司的一个学长跟我说lambda表达式很有用,所以我就去学了。
这东西确实挺厉害的,据说发明他的作者认为一切程序的本质都是函数,后面还用了数学公式证明,不得不说数学厉害的人写程序真的强,随便就弄出一个定理和语法规范,让我们都得跟着学。
lambda表达式语法
// lambda表达式
#include <iostream>
using namespace std;
//正常函数的写法 有名字,有返回值,有参数列表
int fun(int a, int b)
{
return a + b;
}
int main()
{
/* lambda表达式的本质就是一个仿函数(匿名函数)
也就是一个类里面有一个operator()() 重载小括号
它没有名字
*/
//这是lambda表达式的写法,匿名的,前面的[]符号代表正常函数中的fun也就是名字,后面的()符号
//代表函数的参数列表,再后面的->int代表函数的返回值类型,也就是正常函数前面的int,void之类的
//紧接着后面的{}里写函数的主体,也就是函数的具体实现,最后一个()里写调用函数时的传参
// 1.[]函数名字 2.()参数列表 3.->int函数返回值类型 4.{}函数主体
//最后返回的值在最前面用一个变量来接收
int c = [](int a, int b) -> int
{
//函数主体
return a + b;
}(1, 2); // 5.()调用函数传参 在定义时就可以立即匿名调用 但是在其他地方就用不了了
//上面等价于下面
int c2 = fun(1, 2);
cout << c << endl;
//第二种写法 使用自动类型推导 auto将会把它推成一个匿名函数的类型
//也就是把f当作这个匿名函数的名字,那么在其他地方就可以通过f调用到这个匿名函数
auto f = [](int a, int b) -> int
{
//函数主体
return a + b;
};
// f被当作了上面这个匿名函数的名字,通过f调用到了这个匿名函数
int c3 = f(1, 2);
cout << c3 << endl;
return 0;
}
lambda表达式实战-嵌套
// lambda表达式实战-嵌套
#include <iostream>
using namespace std;
int main()
{
// lambda嵌套lambda
// lambda 跟普通类型一样可以作为参数也可以作为返回值 这里就是作为返回值 也叫Currying
//函数编程 用于多线程 并发
//外面的函数体返回的是内部的函数体的内容
int c = [](int n)
{
//内部的函数体可以使用外部的函数体的形参,只要在内部的函数体的[]里加上外部的变量名即可
return [n](int x)
{
//只有一条return语句可以不写返回类型,编译器会推断 其他情况不写就是void
return n + x;
}(1); // 匿名调用内部的函数 1传给x
}(2); // 匿名调用外部的函数 2传给n
cout << c << endl;
//通过自动类型推导为匿名函数加上名字,可以让它在其他地方调用
auto f = [](int n)
{
cout << "n= " << n << endl;
return [n](int x)
{
cout << "x= " << x << endl;
return n + x;
};
};
//调用函数传参 1传给n 2传给x 先传参调用外层,再传参调用里层
int c2 = f(1)(2);
cout << c2 << endl;
return 0;
}
lambda按值捕获-mutable
// mutable-lambda按值捕获
#include <iostream>
using namespace std;
int main()
{
// lambda第三种写法
int t = 10;
//如果想要获取外部(上面)的t的值,那么就不能写在参数列表()里,参数列表是在调用时传的
//要写在[]里,[]是捕获列表,也就是匿名函数的捕获区,在捕获区里写变量名即可捕获外部的变量
//在[]里捕获的变量默认是const类型,无法被修改,只有在()后面加了mutable关键字后[]捕获的值才能被修改
auto f = [t]() mutable
{
return ++t;
};
//在用mutable进行捕获时会单独拷贝出一份地址,这样在里面修改的值会一直保存在函数体里,下一次调用时会依旧使用上一次拷贝的值
//这个值只在当前的匿名函数内部是有效的,就算外部有一个一模一样的匿名函数调用也只拷贝原来变量的值,无法拷贝之前匿名函数的值
// 11
cout << f() << endl;
// 12
cout << f() << endl;
//按值捕获,在函数体内部的修改影响不了外部的值,也就是形参无法修改实参
cout << t << endl;
return 0;
}
lambda按引用捕获
// lambda按引用捕获
#include <iostream>
using namespace std;
int main()
{
//按值捕获
int t = 10;
[t]()
{
cout << t << endl;
}();
auto f = [t]()
{
cout << t << endl;
};
t = 11;
// 10 按值捕获的时候,在捕获时变量的值是多少,捕获的值就是多少,无法捕获到在捕获后面才声明的值
f();
//按引用捕获,传的是指针,可以改变实参
auto f2 = [&t]()
{
t = 13;
};
t = 11;
f2();
// 13 按引用捕获可以捕获到在函数后面才声明的值,并可以改变它,在函数调用前变量的值是多少,引用捕获的值就是多少
//在函数内部修改按引用捕获的值后就可以修改外部变量的值,也就是通过指针改变实参
cout << "&t = " << t << endl;
//捕获列表,可以同时捕获多个变量
int a = 1;
int b = 2;
int c = 3;
int d = 4;
[a, b]()
{
cout << "a = " << a << " b= " << b << endl;
}();
//捕获外部所有的变量,按值捕获
[=]()
{
cout << "a = " << a << " b= " << b << " c = " << c << " d= " << d << " t= " << t << endl;
}();
//捕获外部所有的变量,按引用捕获
[&]()
{
cout << "&a = " << a++ << " &b= " << ++b << " &c = " << c++ << " &d= " << d++ << " &t= " << ++t << endl;
}();
//既按值捕获,又按引用捕获
[a, b, &c, d, &t]()
{
cout << "a = " << a << " b= " << b << " &c = " << c++ << " d= " << d << " &t= " << ++t << endl;
}();
return 0;
}
lambda实战
// lambda实战
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void test01()
{
//使用动态数组遍历判断奇偶数的普通写法
vector<int> v = {1, 2, 3, 4, 5};
for (int i = 0; i < v.size(); i++)
{
if (v[i] % 2 == 0)
{
cout << v[i] << "是偶数" << endl;
}
else
{
cout << v[i] << "是奇数" << endl;
}
}
cout<<"-------------------------------"<<endl;
//使用lambda的写法,在for_each的最后面使用匿名函数遍历
//在每一次遍历完后将元素传递给参数列表的n
for_each(v.begin(), v.end(), [](int n) {
if (n % 2 == 0)
{
cout << n << "是偶数" << endl;
}
else
{
cout << n << "是奇数" << endl;
}
});
}
int main()
{
test01();
return 0;
}
lambda-function函数对象包装器
// lambda-function函数对象包装器
#include <iostream>
using namespace std;
//需要包含这个头文件
#include <functional>
int test01(int n)
{
cout << n << endl;
return n;
}
class CTest
{
public:
//构造函数
CTest() {}
//普通成员函数
int Mytest(int n)
{
cout << n << endl;
return n;
}
//仿函数的写法
int operator()(int n)
{
cout << n << endl;
return n;
}
};
int main()
{
//普通函数
test01(1);
//函数对象包装器,第一个int是返回值的类型,第二个()里的int是参数列表
function<int(int)> f = test01;
//将普通函数包装成一个叫做f的对象,可以直接使用f进行调用
f(123);
//支持4种函数的包装:1.普通函数 2.匿名函数 3.类的成员函数 4.仿函数(重载了()运算符的函数)
//匿名函数包装
function<int(int)> f2 = [](int n) -> int
{
cout << n << endl;
return n;
};
f2(456);
//类的成员函数包装 因为类的成员函数传递的是类的对象的引用,所以在参数列表里要写上类的指针
function<int(CTest *, int)> f3 = &CTest::Mytest;
CTest t;
//调用包装的函数
f3(&t, 789);
//仿函数的包装
function<int(CTest *, int)> f4 = &CTest::operator();
//调用仿函数
f4(&t, 123);
return 0;
}
lambda-bind机制
// bind机制
#include <iostream>
#include <functional>
using namespace std;
void test01(int a, int b, int c, int d)
{
cout << a << " " << b << " " << c << " " << d << endl;
}
int main()
{
//假设有一个函数有很多的参数,而每次调用的时候都想传递1 2 3 4 给参数 a b c d
//那么就可以利用bind把调用的过程用参数和函数名绑定变成一个对象
auto a = bind(test01, 1, 2, 3, 4);
//那么在下次调用时就可以直接通过调用对象的方式调用绑定的函数,函数默认传参
a();
//假设在绑定时第2个函数的参数暂时不知道,可以用placeholders使参数待定,等待调用的时候再根据传递的值进行绑定
//_1代表第一个参数,也就是调用的时候先传的第一个参数
auto foo2 = bind(test01, 1, placeholders::_1, 3, 4);
//这里的2是传给了placeholders::_1
foo2(2);
//使多个参数待定
auto foo3 = bind(test01, 1, placeholders::_2, placeholders::_1, 4);
//这里的1是传给了placeholders::_1, 2是传给了placeholders::_2
//这样的话就可以使函数调用的顺序不同
foo3(1, 2);
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 一生雾梦!
评论
ValineDisqus