【C++11】包装器:function与bind

前言:

        上文我们学了C++11中一个新的表达式:Lambda表达式。Lambda表达式可以在函数内部定义,其本质是仿函数【C++11】Lambda表达式-CSDN博客

        本文我们来学习C++11的下一个新语法:包装器

function

function的定义为:

template <class T>
class function; // undefined

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

         function的一个类模板,也是一个包装器。function的实例对象可以包装调用一切可调用对象。但若function的对象为被初始化,进行调用就会报错(抛异常:std::bad_function_call)

        function模板类放在<functional>头文件中

        使用function包装不同的可调用对象,就可以用相同的方式调用不同可调用对象

        function的本质其实也是仿函数

  使用细节如下:

#include<iostream>
#include<functional>
using namespace std;
//function可以包装一切可调用对象,其本质是仿函数

//template <class Ret, class... Args>
//class function<Ret(Args...)>;

int f(int a, int b)
{
	return a + b;
}


struct Functor
{
	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;
	}

	double plusd(double a, double b)
	{
		return (a + b) * _n;
	}

private:
	int _n;
};


int main()
{
	//可包装一切可调用对象
	function<int(int, int)> a = f;
	function<int(int, int)> b = Functor();
	function<int(int, int)> c = [](int a, int b) {return a * 10 + b; };

	cout << a(1, 2) << endl;
	cout << b(1, 2) << endl;
	cout << c(1, 2) << endl;

	//既然说function可以包装一切可调用对象,那么类成员函数既然也可以被包装
	
	//包装静态成员函数: 1.必须要指定类域,2.非静态成员必须要取地址,但是静态不用,为了同一格式建议加上
	function<int(int, int)> x = &Plus::plusi;
	cout << x(1, 2) << endl;


	//包装非静态成员函数:1.必须要指定类域 2.必须取地址 3.非静态成员函数有this指针
	function<int(Plus*,int, int)> y = &Plus::plusd;
	Plus p;
	cout << y(&p, 1, 2) << endl;


	//也可以直接传对象,因为:类成员函数的调用是通过 .* 运算符实现的
	//前面传指针,也是先将指针解引用为对象,再通过 .* 运算符调用成员函数
	function<int(Plus, int, int)> z = &Plus::plusd;
	cout << z(p, 1, 2) << endl;


	//与直接传对象类似,引用是为了减少拷贝,提高效率
	function<int(Plus&&, int, int)> q = &Plus::plusd;
	cout << q(move(p), 1, 2) << endl;

	function<int(Plus&, int, int)> w = &Plus::plusd;
	cout << w(p, 1, 2) << endl;
}

        包装其他可调用对象,没什么区别,唯独需要注意当我们在包装类成员函数时有一些不同:

        包装静态成员函数时:1.必须要指定类域,2.非静态成员必须要取地址,但是静态不用,为了统一格式建议加上。

        包装非静态成员函数时:1.必须要指定类域 2.必须取地址 3.非静态成员函数有this指针。

练习:150. 逆波兰表达式求值 - 力扣(LeetCode)

#include<iostream>
#include<vector>
#include<functional>
#include<map>
#include<stack>
#include<string>
using namespace std;


class Solution
{
public:
    int evalRPN(vector<string>& tokens)
    {
        stack<int> s;
        //使用map将运算符与运算逻辑匹配
        //使用Lambda实现运算逻辑
        //利用function包装Lambda
        map<string, function<int(int, int)>> m =
        {
            {"+",[](int a,int b) {return a + b; }},
            {"-",[](int a,int b) {return a - b; }},
            {"*",[](int a,int b) {return a * b; }},
            {"/",[](int a,int b) {return a / b; }}
        };

        for (auto& e : tokens)
        {
            //当count返回不是0,说明当前的字符是"+ - * /"中的一个
            //取两个操作数,进行相应的运算
            if (m.count(e))
            {
                int left = s.top();
                s.pop();
                int right = s.top();
                s.pop();

                //operator[]返回function对象的引用
                //function对象直接调用Lambda表达式
                int num = m[e](right, left);
                s.push(num);
            }
            else
            {
                s.push(stoi(e));//stoi函数:将字符串转化为整型
            }
        }

        return s.top();
    }
};


int main()
{
    vector<string> arr = { "4","13","5","/","+" };
    cout << Solution().evalRPN(arr);
}

bind

bind的定义:

simple(1)
template <class Fn, class... Args>
 /* unspecified */ bind (Fn&& fn, Args&&... args);
 
with return type (2)
template <class Ret, class Fn, class... Args>
 /* unspecified */ bind (Fn&& fn, Args&&... args);

        bind的一个模板函数,也是一个包装器,可以将其看作一个函数适配器。bind接收一个函数处理后会返回一个可调用对象。bind的功能:调整函数参数顺序、控制函数参数个数

        调用bind的一般形式为:auto newCallable = bind(callable,arg_list);callable是函数地址,newCallable是可调用对象,调用newCallable会将arg_list的参数传给callable,然后调用callable。bind的底层和function一样都是仿函数,所以也可以用function代替auto来接收bind的返回对象

        在arg_list中有一个特殊概念:占位符。假设有n个参数,那占位符至多有n个:_1 , _2 , _3 , ....... , _n。_1代表第一个参数,_2代表第二个参数,........ ,_n代表第n个参数。这些占位符放在placeholders命名空间

使用样例:

#include<iostream>
#include<functional>
using namespace std;
//bind的占位符都放在这个命名空间里
using namespace placeholders;

//bind有两个作用:1.交换参数顺序 2.绑定参数,控制参数个数(常用)
//bind的本质和function一样都是仿函数


class Plus
{
public:
	static int plusi(int a, int b)
	{
		return a + b;
	}

	double plusd(double a, double b)
	{
		return a + b;
	}
};


int add(int a, int b)
{
	return a - b;
}

int add1(int a, int b, int c)
{
	return a * 10 + b - c;
}

int main()
{
	//调整参数顺序(没什么用处)
	auto e = bind(add, _1, _2);
	cout<<e(2, 3)<<endl;

	auto x = bind(add, _2, _1);
	cout << x(2, 3) << endl;

	//调整参数个数(常用)
	//直接绑定第1、2、3的参数,传参的时候只用传未绑定的参数
	auto c = bind(add1, 10, _1, _2);
	cout << c(1,2) << endl;

	auto d = bind(add1, _1, 10, _2);
	cout << d(1, 2) << endl;

	auto w = bind(add1, _1, _2, 10);
	cout << w(1, 2) << endl;

	//bind常用于绑定固定参数
	//function<double(Plus*,double, double)> f = &Plus::plusd;
	function<double(double, double)> f = bind(&Plus::plusd, Plus(), _1, _2);
	cout<<f(1, 2)<<endl;

}

        总的来看,bind有用的功能主要是控制函数参数个数常用于绑定一些固定的参数

评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值