【C++】C++11:lambda、包装器
C++11:lambda、包装器
1、lambda表达式
1.1 lambda表达式语法
lambda 表达式本质是一个匿名函数对象,跟普通函数不同的是他可以定义在函数内部。我们⼀般是用auto或者模板参数定义的对象去接收lambda 对象。
#include <iostream>
using namespace std;
int main()
{
// [] 捕获列表,() 参数,-> 返回值(可省略),{} 函数体
auto func = []() {
cout << "这是lambda匿名函数" << endl;
};
// 调用lambda
// 输出这是lambda匿名函数
func();
return 0;
}
lambda表达式的格式:[capture-list] (parameters)-> return type { function boby }
[capture-list]:捕捉列表,捕捉外面的变量供lambda函数使用,捕捉列表为空也不能省略
(parameters):lambda的形参列表,用来传参,与普通函数的参数列表功能类似,如果不需要参数传递,则可以连同()一起省略
->return type:返回值类型,指定返回值类型,一般返回值类型明确情况下可以省略,由编译器对返回类型进行推导
{function boby}:函数体,和普通函数大括号完全一样,写逻辑、循环、判断、return、cout 输出全都放这里。
eg:
int main()
{
auto add1 = [](int x, int y)->int{return x + y;};
cout << add1(1, 2) << endl; // 输出3
// 1、捕捉为空也不能省略
// 2、参数为空可以连同()一起省略
// 3、返回值可以省略,可以通过返回对象自动推导
// 4、函数体不能省略
auto func1 = []
{
cout << "hello" << endl;
return 0;
};
func1(); // 输出hello
int a = 0, b = 1;
auto swap1 = [](int& x, int& y)
{
int tmp = x;
x = y;
y = tmp;
};
swap1(a, b);
cout << a << ":" << b << endl; // 输出1:0
return 0;
}
1.2 lambda和sort排序
使用sort排序:
struct Goods
{
string _name; // 名字
double _price; // 价格
int _evaluate; // 评价
// ...
Goods(const char* str, double price, int evaluate)
:_name(str)
, _price(price)
, _evaluate(evaluate)
{
}
};
// 按价格升序
struct Compare1
{
bool operator()(const Goods& gl, const Goods& gr)
{
return gl._price < gr._price;
}
};
// 按价格降序
struct Compare2
{
bool operator()(const Goods& gl, const Goods& gr)
{
return gl._price > gr._price;
}
};
// 按评价分数降序
struct Compare3
{
bool operator()(const Goods& gl, const Goods& gr)
{
return gl._evaluate > gr._evaluate;
}
};
int main()
{
vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3}, { "菠萝", 1.5, 4 } };
sort(v.begin(), v.end(), Compare1());
sort(v.begin(), v.end(), Compare2());
sort(v.begin(), v.end(), Compare3());
}
使用sort后可读性较差,逻辑复杂,使用lambda表达式修改
#include<vector>
#include<algorithm>
struct Goods
{
string _name; // 名字
double _price; // 价格
int _evaluate; // 评价
// ...
Goods(const char* str, double price, int evaluate)
:_name(str)
, _price(price)
, _evaluate(evaluate)
{
}
};
int main()
{
// 价格降序
auto priceLess = [](const Goods& gl, const Goods& gr)
{
return gl._price < gr._price;
};
sort(v.begin(), v.end(), priceLess);
sort(v.begin(), v.end(), [](const Goods& gl, const Goods& gr){
return gl._price > gr._price;
});
sort(v.begin(), v.end(), [](const Goods& gl, const Goods& gr) {
return gl._evaluate > gr._evaluate;
});
sort(v.begin(), v.end(), [](const Goods& gl, const Goods& gr) {
return gl._evaluate < gr._evaluate;
});
}
用lambda表达式后,不用单独写一个struct仿函数,不需要额外定义类
1.3 捕捉列表
lambda表达式中默认只能用lambda 函数体和参数中的变量,如果想用外层作用域中的变量就需要进行捕捉
第一种:在捕捉列表中显示的传值捕捉([var])和传引用捕捉([&var]),捕捉的多个变量用逗号分割
int x = 0;
int main()
{
// 只能用当前lambda局部域和捕捉的对象和全局对象
int a = 0, b = 1, c = 2, d = 3;
auto func1 = [a, &b]() mutable
{
// 值捕捉的变量不能修改,引用捕捉的变量可以修改
//a++;
b++;
x++;
int ret = a + b;
return ret;
};
cout << func1() << endl; // 输出:3
}
第二种:在捕捉列表中隐式捕捉,[=]表示隐式值捕捉,[&]表示引用方式传递捕捉所有父作用域中的变量,这样我们lambda表达式中⽤了那些变量,编译器就会自动捕捉那些变量
int main()
{
int a = 0, b = 1, c = 2, d = 3;
// 隐式值捕捉
// 用了哪些变量就捕捉哪些变量
auto func2 = [=]
{
int ret = a + b + c;
return ret;
};
cout << func2() << endl; // 输出3
// 隐式引用捕捉
// 用了哪些变量就捕捉哪些变量
auto func3 = [&]
{
a++;
c++;
d++;
};
func3();
// 输出1 1 3 4
cout << a << " " << b << " " << c << " " << d << endl;
}
第三种:在捕捉列表中混合使用隐式捕捉和显示捕捉,[=, &x]表示其他变量隐式值捕捉,x引用捕捉;[&, x, y]表示其他变量引用捕捉,x和y值捕捉
int main()
{
// 只能用当前lambda局部域和捕捉的对象和全局对象
int a = 0, b = 1, c = 2, d = 3;
// 混合捕捉1
auto func4 = [&, a, b] //其他都是引用捕捉,a和b是值捕捉,&不能放到中间
{
//a++;
//b++;
c++;
d++;
return a + b + c + d;
};
func4();
// 输出0 1 3 4
cout << a << " " << b << " " << c << " " << d << endl;
// 混合捕捉2
auto func5 = [=, &a, &b] //其他都是值捕捉,a和b是引用捕捉
{
a++;
b++;
/*c++;
d++;*/
return a + b + c + d;
};
func5();
// 输出1 2 3 4
cout << a << " " << b << " " << c << " " << d << endl;
return 0;
}
注意:
值传递方式捕捉的变量默认情况下是被const修饰的,也就是说传值捕捉的变量不能修改,如果想修改可以在参数列表的后面加mutable取消其常量性,例如:
auto func7 = [=]()mutable
{
a++; // a可修改
b++;
c++;
d++;
return a + b + c + d;
};
cout << func7() << endl;
cout << a << " " << b << " " << c << " " << d << endl;
语法上捕捉列表可以有多个项组成,例如[&, a, b],其他都是引用捕捉,a和b是值捕捉,&只能放在列表的开头,[a, &, b]是错误的
捕捉列表不允许变量重复传递,否则会导致编译出错,例如[=, a],出现了两个值捕捉,导致重复
2、function
std::function 是一个类模板,是C++提供的函数包装器类模板,不管是普通函数、函数指针、仿函数、lambda、bind绑定出来的可调用对象,std::function 相当于一个统一的盒子,能把它们全部装进去,统一形成一种类型
template < class T>
class function; // undefined
template < class Ret, class... Args>
class function<Ret(Args...)>;
//eg:接收两个int参数,返回int的可调用对象盒子
function<int(int, int)>
函数指针、仿函数、lambda等可调用对象的类型各不相同,std::function 的优势就是统一类型,对他们都可以进行包装,这样在很多地方就方便声明可调用对象的类型
#include<functional>
// 计算两数相加
int f(int a, int b)
{
return a + b;
}
// 仿函数结构体
struct Functor
{
public:
int operator() (int a, int b)
{
return a + b;
}
};
// 包含多种函数
class Plus
{
public:
// 构造函数
Plus(int n = 10)
:_n(n)
{}
// 静态成员函数
static int plusi(int a, int b)
{
return a + b;
}
// const普通成员函数
double plusd(double a, double b) const
{
return (a + b) * _n;
}
private:
int _n;
};
int main()
{
//函数指针
// void(*pf)(int) = nullptr;
// 包装各种可调用对象
function<int(int, int)> f1 = f; // 全局函数
function<int(int, int)> f2 = Functor(); // 仿函数
function<int(int, int)> f3 = [](int a, int b) {return a + b; }; // lamdba
cout << f1(1, 1) << endl; // 2
cout << f2(1, 1) << endl; // 2
cout << f3(1, 1) << endl; // 2
// 把function放进vector容器中
vector<function<int(int, int)>> vf = { f , Functor(),[](int a, int b) {return a + b; } };
for (auto& f : vf)
{
cout << f(1, 1) << endl; // 输出2
}
// 包装静态成员函数
// 成员函数要指定类域并且前面加&才能获取地址
function<int(int, int)> f4 = &Plus::plusi;
cout << f4(1, 1) << endl;
// 普通成员函数隐藏第一个参数,即this参数,所以要加上Plus*
function<double(Plus*, double, double)> f5 = &Plus::plusd;
Plus ps;
cout << f5(&ps, 1.1, 1.1) << endl; // 输出22
// 第二种包装plusd,接收const对象引用
// const Plus& 可以接收普通对象、临时对象,不用传指针
//function<double(Plus, double, double)> f6 = &Plus::plusd;
function<double(const Plus&, double, double)> f6 = &Plus::plusd;
cout << f6(Plus(), 1.1, 1.1) << endl; // Plus()是临时对象,匹配const引用,输出22
cout << f6(ps, 1.1, 1.1) << endl; // 传入局部对象ps,输出22
// 右值引用版本Plus&&:仅能接收临时对象、std::move转换后的右值对象
function<double(Plus&&, double, double)> f7 = &Plus::plusd;
cout << f7(Plus(), 1.1, 1.1) << endl; // Plus()临时右值,匹配Plus&&,输出22
cout << f7(move(ps), 1.1, 1.1) << endl; // move把ps转为右值,匹配Plus&&,输出22
return 0;
}
总结:三种传对象的方式区别:
Plus*:传对象地址
const Plus&:万能,普通对象、临时对象都能接
Plus&&:只接收临时右值,或者 move 转换后的对象
3、bind
bind是一个函数模板,也是一个可调用对象的包装器,接收任意可调用对象,返回一个新的可调用对象,它可以用来调整参数个数和参数顺序
// 版本1:自动推导返回值
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
// 版本2:手动指定返回值
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
bind的一般形式auto newCallable = bind(callable,arg_list);
newCallable:可调用对象
callable:代表原始可调用对象,就是你想要改装的函数
arg_list:是逗号分隔的参数,分两种类型:1、固定常量(数字 / 变量 / 对象)2、占位符 _1 _2 _3
_1为newCallable的第⼀个参数,_2为第⼆个参数,以此类推
#include<functional>
using placeholders::_1;
using placeholders::_2;
using placeholders::_3;
int Sub(int a, int b)
{
return (a - b) * 10;
}
int SubX(int a, int b, int c)
{
return (a - b - c) * 10;
}
int main()
{
cout << Sub(10, 5) << endl;
// bind 本质返回的一个仿函数对象
// 调整参数顺序(不常用)
// _1代表第一个实参
// _2代表第二个实参
// ...
// _2填给Sub的第一个形参a,_1 填给 Sub 的第 2 个形参 b
auto newSub1 = bind(Sub, _2, _1);
cout << newSub1(10, 5) << endl;
// 调整参数个数
// _1填给Sub的a,常量10固定填给Sub的b
auto newSub2 = bind(Sub, _1, 10);
cout << newSub2(20) << endl;
// 常量5固定填给Sub的a,_1填给Sub的b
auto newSub3 = bind(Sub, 5, _1);
cout << newSub3(20) << endl;
// 常量5填给SubX的a,_1填给SubX的b,_2填给SubX的c
auto newSub4 = bind(SubX, 5, _1, _2);
cout << newSub4(10, 20) << endl;
// _1填给SubX的a,常量5填给SubX的b,_2填给SubX的c
auto newSub5 = bind(SubX, _1, 5, _2);
cout << newSub5(10, 20) << endl;
// _1填给SubX的a,_2填给SubX的b,常量5填给SubX的c
auto newSub6 = bind(SubX, _1, _2, 5);
cout << newSub6(10, 20) << endl;
写_n:调用新函数时传入的第 n 个实参会填充到这个原函数形参位置
更多推荐

所有评论(0)