lambda表达式与包装器详解

目录

一.lambda

1.1仿函数

1.2lambda表达式语法

二.包装器

2.1为何需要包装器

2.2使用介绍

2.3bind


一.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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值