目录
一.lambda
1.1仿函数
先看这个例子:
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._evaluate < gr._evaluate;
}
};
int main()
{
vector<Goods> v = { Goods("苹果", 2.5, 5), { "香蕉", 3, 4 }, { "梨", 2.2, 3 }, { "菠萝", 1.9, 4 } };
sort(v.begin(), v.end(), Compare1());
return 0;
}
若一个自定义类型里面的数据很多,就要写很多的类去比较,可能容易写错,不易发觉。
因此,在
C++11
语法中出现了
Lambda
表达式,对于上面的场景,可以运用Lambda。
例如:
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){
return g1._evaluate < g2._evaluate; });
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){
return g1._evaluate > g2._evaluate; });
1.2lambda表达式语法
lambda
表达式书写格式:
[capture-list] (parameters) mutable -> return-type { statement
}
1. lambda
表达式各部分说明
[capture-list] :
捕捉列表
,该列表总是出现在
lambda
函数的开始位置,
编译器根据[]来
判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变
量供lambda
函数使用。
(parameters)
:参数列表。与
普通函数的参数列表一致
,如果不需要参数传递,则可以
连同
()
一起省略
mutable
:默认情况下,
lambda
函数总是一个
const
函数,
mutable
可以取消其常量
性。使用该修饰符时,参数列表不可省略
(
即使参数为空
)
。
->returntype
:
返回值类型。用追踪返回类型形式声明函数的返回值类型,
没有返回
值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推
导。
{statement}
:函数体
。在该函数体内,
除了可以使用其参数外,还可以使用所有捕获
到的变量。
举栗子看一下:
上述例子可以看出,
lambda
表达式实际上可以理解为无名函数,该函数无法直接调
用,如果想要直接调用,可借助
auto
将其赋值给一个变量。
再比如:
捕获列表说明:
其中也可搭配着使用,例如:
一些补充的点:
语法上捕捉列表可由多个捕捉项组成,并以逗号分割。
捕捉列表不允许变量重复传递,否则就会导致编译错误。 比如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复
在块作用域中的lambda函数仅能捕捉父作用域中局部变量
函数对象与
lambda
表达式 :
class Rate
{
public:
Rate(double rate) : _rate(rate)
{
cout << "Rate" << endl;
}
double operator()(double money, int year)
{
return money * _rate * year;
}
private:
double _rate;
};
int main()
{
double rate = 0.49;
Rate r1(rate);
cout << r1(10000, 2) << endl;
// lamber
auto r2 = [=](double monty, int year)->double{return monty*rate*year; };
cout << r2(10000, 2) << endl;
return 0;
}
结果:
Rate
9800
9800
从使用方式上来看,函数对象与
lambda
表达式完全一样。
函数对象将
rate
作为其成员变量,在定义对象时给出初始值即可,
lambda
表达式通过捕获列表可
以直接将该变量捕获到。
在底层编译器对于lambda表达式的处理方式,就是按照函数对象的方式处理的,如
果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。
补充:
lambda表达式之间不能相互赋值,即使看起来类型相同 。
但是允许使用lambda表达式拷贝构造
但是允许使用lambda表达式拷贝构造。
函数类名称lambda+ uuid,每个都是不一样的。
二.包装器
包装器:function
function
包装器 也叫作适配器。
C++
中的
function
本质是一个类模板,也是一个包装器。
2.1为何需要包装器
先看一个简单的例子:
template<class F, class T>
T useF(F f, T x)
{
static int count = 0;
cout << "count:" << ++count << endl;
cout << "count:" << &count << endl;
return f(x);
}
double Fun(double i)
{
return i / 2;
}
struct Functor
{
double operator()(double d)
{
return d / 3;
}
};
int main()
{
cout << useF(Fun, 9.9) << endl;// 函数名
cout << useF(Functor(), 11.11) << endl;// 函数对象
cout << useF([](double d)->double{ return d / 4; }, 11.11) << endl; // lamber表达式
return 0;
}
结果:
count:1
count:003FC13C
4.95
count:1
count:003FC148
3.70333
count:1
count:003FC14C
2.7775
会实例化出三个不同的函数,可能会导致模板的效率低下,而包装器可以很好的适应上面的情况。
2.2使用介绍
Ret:被调用函数的返回类型,
Args...:被调用函数的形参
例如:
int main()
{
// 函数名
function<double(double)> func1 = Fun;
cout << func1(2) << endl;
// 函数对象
function<double(double)> func2 = Functor();
cout << func2(2.2) << endl;
// lamber表达式
function<double(double)> func3 = [](double d)->double { return d / 4; };
cout << func3(3.14) << endl;
return 0;
}
结果:
1
0.733333
0.785
也可以包装类中的静态成员函数和非静态成员函数。
例如:
class AA
{
public:
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return a + b;
}
};
int main()
{
function<int(int, int)> func4 = &AA::plusi;//静态成员函数
cout << func4(100, 200) << endl;
function<double(AA, double, double)> func5 = &AA::plusd;//非静态
cout << func5(AA(), 100.11, 200.11) << endl;
}
2.3bind
介绍:
std::bind
函数定义在头文件中,
是一个函数模板,它就像一个函数包装器
(
适配器
)
,
接受一个可
调用对象(
callable object
),生成一个新的可调用对象来
“
适应
”
原对象的参数列表
。一般而
言,我们用它可以把一个原本接收
N
个参数的函数
fn
,通过绑定一些参数,返回一个接收
M
个(
M
可以大于
N
,但这么做没什么意义)参数的新函数。同时,使用
std::bind
函数还可以实现参数顺
序调整等操作。
例如:上面的代码中function<double(AA, double, double)> func5 = &AA::plusd;//非静态
含有三个参数,可对其进行绑定。
function<int(int, int)> func4 = bind(&AA::plusi, AA(99), placeholders::_1, placeholders::_2);
举例:
class AA
{
public:
AA(int x = 2)
:_x(x)
{}
int plusi(int a, int b)
{
return (a - b) * _x;
}
private:
int _x;
};
int main()
{
function<int(int, int)> func1 = bind(&AA::plusi, AA(10), placeholders::_1, placeholders::_2);//非静态成员函数,减少了一个参数
cout << func1(2, 3) << endl;
function<int(int)> func2 = bind(&AA::plusi, AA(10),10, placeholders::_1);//一个参数
cout << func2(100) << endl;
function<int(int, int)> func3 = bind(&AA::plusi, AA(10), placeholders::_2, placeholders::_1);//调整参数的顺序
cout << func3(2, 3) << endl;
}
结果:
-10
-900
10
可以将
bind
函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对
象来
“
适应
”
原对象的参数列表。
使用场景:
double Fun(int a,int b)
{
return a+b;
}
struct Functor
{
double operator()(int d,int p)
{
return d / p;
}
};
class AA
{
public:
AA(int x = 2)
:_x(x)
{}
int plusi(int a, int b)
{
return (a - b) * _x;
}
private:
int _x;
};
int main()
{
map<string,function<int(int, int)>> opFuncMap =
{
{ "普通函数指针", Fun },
{ "函数对象", Functor() },
{ "成员函数指针", std::bind(&AA::plusi, AA(10), placeholders::_1, placeholders::_2) }//将三个参数减少为2个
};
return 0;
}