C++--Lambad和包装器

1.Lambad

1.1 语法及其用法

lambad表达式:[捕捉列表](参数列表)mutable->返回值类型{函数体}

捕捉列表:该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。

参数列表:与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略

mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。

返回值类型:用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。(连同->一起省略)

函数体:在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。

lambad最具特点的地方当属捕捉列表。

	int a = 0;
	int b = 1;

	//最简单的函数
	auto fun1 = [] {};

	auto fun2 = [](int x, int y) {return x + y; };

	auto fun3 = [](int x, int y)->int {return x + y; };

	auto fun4 = [a, b] (int& x){ x = a + b; };

	//	//mutable 只是让传值捕捉变量const属性去掉了
	auto Swap2 = [a, b]()mutable{
		int tmp = a;
		a = b;
		b = tmp;
	};
	// 用引用的方式捕捉
	auto Swap2 = [&a, &b]{
		int tmp = a;
		a = b;
		b = tmp;
	};

//通过上述例子可以看出,lambda表达式实际上可以理解为无名函数,该函数无法直接调
//用,如果想要直接调用,可借助auto将其赋值给一个变量

 1.2 lambad捕获列表

捕捉列表描述了上下文中那些数据可以被lambda使用,以及使用的方式传值还是传引用。

  • [var]:表示值传递方式捕捉变量var
  • [=]:表示值传递方式捕获所有父作用域中的变量(包括this)
  • [&var]:表示引用传递捕捉变量var
  • [&]:表示引用传递捕捉所有父作用域中的变量(包括this)

父作用域指的是lambad函数的语句块,捕捉列表在语法上支持多个捕捉项(例下,标识符=&要写在最前面,不能写在后面),不允许变量重复传递。

	int c =2, d=3, e=4, f=5, g=6, ret;
	// 传值捕捉全部对象
	auto Func1 = [=]{
		return c + d*e / f + g;
	};

	cout << Func1() << endl;

	// 传引用捕捉全部对象
	auto Func2 = [&]{
		ret = c + d*e / f + g;
	};

	Func2();
	cout << ret << endl;

	// 混着捕捉
	auto Func3 = [c, d, &ret]{
		ret = c + d;
	};

	Func3();
	cout << ret << endl;

	// ret传引用捕捉 其他全部传值捕捉
	auto Func4 = [=, &ret]{
		ret = c + d*e / f + g;
		//c = 1;
	};

1.3 lambad底层

lambad不支持相互赋值,即使看起来类型相同

 这是因为lambad的实现底层是仿函数,每个lambad函数都是不一样的。仿函数名称是lambad+uuid,uuid的生成是唯一的!

可以通过反汇编来查看。

1.4 lambad的好处

C++11之前,每次为了实现一个algorithm算法, 都要重新去写一个类(仿函数),如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名, 这些都给编程者带来了极大的不便。而lambad表达式的出现极大地方便了方法的编写。auto fun实际上就是一个对象。

 2. 包装器

2.1 包装器的引入

ret = func(x);

上面func可能是什么呢?那么func可能是函数名?函数指针?函数对象(仿函数对象)?也有可能 是lamber表达式对象?所以这些都是可调用的类型!如此丰富的类型,可能会导致模板的效率低下

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 f(double i)
{
	return i / 2;
}
struct Functor
{
	double operator()(double d)
	{
		return d / 3;
	}
};


auto fun2 = [] (double a){return 6.0 / 1.2; };

int main()
{
	useF(f, 1.0);
	useF(Functor(), 1.0);
	useF(fun2, 1.0);
}

 通过上面的程序验证,我们会发现useF函数模板实例化了三份。

由此引入包装器,function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。

std::function在头文件  类模板原型如下:

template <class Ret,class ... Args>

class function<Ret(Args...)>;

模板参数说明: Ret: 被调用函数的返回类型, Args…:被调用函数的形参

2.2 使用方法 

template<class F, class T>
T useF(F f, T x, T y)
{
	static int count = 0;
	cout << "count:" << ++count << endl;
	cout << "count:" << &count << endl;
	return f(x,y);
}

int f(int x, int y)
{
	return x + y;
}

struct Functor
{
	int operator()(int x, int y)
	{
		return x + y;
	}
};

class wzz
{
public:
	static int pluss(int x, int y)
	{
		return x + y;
	}
	int plusi(int x, int y)
	{
		return x + y;
	}
};

int main()
{
	int a = 0;
	int b = 1;
	//普通函数包装
	function<int(int, int)> f1 = f;
	cout << f1(1, 2)<<endl;
	//仿函数包装
	function<int(int, int)> f2 = Functor();
	cout << f2(1, 2) << endl;
	//成员函数 静态包装,&可加可不加
	function<int(int, int)> f3 = &wzz::pluss;
	cout << f3(1, 2) << endl;
	//成员函数 非静态包装,有this指针!而且包装plusi时必须取地址加&
	function<int(wzz, int, int)> f4 = &wzz::plusi;
	cout << f4(wzz(),1, 2) << endl;
	//lambad函数对象静态包装
	function<int(int, int)> f5 = [](int x, int y) {return x + y; };//必须要有足够
//的参数
	cout << f5(1, 2) << endl;

}
//用以下代码验证使用fuction后,useF函数模板只实例化了一份
int main()
{
	int a = 0;
	int b = 1;
	//普通函数包装
	function<int(int, int)> f1 = f;
	useF(f1, 2, 2);
	//仿函数包装
	function<int(int, int)> f2 = Functor();
	useF(f2, 2, 2);
	//成员函数 静态包装,&可加可不加
	function<int(int, int)> f3 = &wzz::pluss;
	useF(f3, 2, 2);
	//成员函数 非静态包装,有this指针!而且包装plusi时必须取地址加&
	function<int(wzz, int, int)> f4 = &wzz::plusi;
	//lambad函数对象静态包装
	function<int(int, int)> f5 = [](int x, int y) {return x + y; };//必须要有足够的参数
	useF(f5, 2, 2);

}

 但是我们发现如果对非静态成员函数包装时,它的参数是会多出一个对象的,和其他的函数参数不一致,该怎么变一致?接下来介绍bind。

3.bind

std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可 调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。一般而 言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M 可以大于N,但这么做没什么意义)参数的新函数。同时,使用std::bind函数还可以实现参数顺 序调整等操作。

// 原型如下:
template <class Fn, class... Args>
bind (Fn&& fn, Args&&... args);

// with return type (2) 

template <class Ret, class Fn, class... Args>
bind (Fn&& fn, Args&&... args);

可以将bind函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对
象来“适应”原对象的参数列表。
调用bind的一般形式:auto newCallable = bind(callable,arg_list);
其中,newCallable本身是一个可调用对象,arg_list是一个逗号分隔的参数列表,对应给定的
callable的参数。当我们调用newCallable时,newCallable会调用callable,并传给它arg_list中
的参数。
arg_list中的参数可能包含形如_n的名字,其中n是一个整数,这些参数是“占位符”,表示
newCallable的参数,它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对
象中参数的位置:_1为newCallable的第一个参数,_2为第二个参数,以此类推。
#include <functional>
int Plus(int a, int b)
{
 return a + b;
}
class Sub
{
public:
 int sub(int a, int b)
 {
 return a - b;
 }
};
int main()
{
 //表示绑定函数plus 参数分别由调用 func1 的第一,二个参数指定
 std::function<int(int, int)> func1 = std::bind(Plus, placeholders::_1, 
placeholders::_2);
 //auto func1 = std::bind(Plus, placeholders::_1, placeholders::_2);
 //func2的类型为 function<void(int, int, int)> 与func1类型一样
 //表示绑定函数 plus 的第一,二为: 1, 2
 auto  func2 = std::bind(Plus, 1, 2);   
 cout << func1(1, 2) << endl;
 cout << func2() << endl;
 Sub s;
 // 绑定成员函数
 std::function<int(int, int)> func3 = std::bind(&Sub::sub, s, 
placeholders::_1, placeholders::_2);
// 参数调换顺序
std::function<int(int, int)> func4 = std::bind(&Sub::sub, s, 
placeholders::_2, placeholders::_1);
 cout << func3(1, 2) << endl; 
 cout << func4(1, 2) << endl;
 return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_w_z_j_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值