【C++】C++11中的包装器和绑定器

目录

一、function包装器

1.1 可调用对象

1.2 概念

1.3 应用场景

二、bind绑定器


一、function包装器

1.1 可调用对象

我们平时使用的普通函数、函数指针、仿函数和Lambda表达式都是可调用对象,它们不仅可以作为其他函数的参数传入,还可以作为其他函数的返回值

虽然这些可调用对象的使用方法基本都差不多,但其定义方式大有不同,例如:

void swap(int& a, int& b) //普通函数
{
	int tmp = a;
	a = b;
	b = tmp;
}

void(*swap_p)(int&, int&) = swap; //函数指针

class Swap
{
public:
	void operator()(int& a, int& b) //仿函数
	{
		int tmp = a;
		a = b;
		b = tmp;
	}
};

auto lam_swap = [](int& a, int& b)-> void { //Lambda表达式
	int tmp = a;
	a = b;
	b = tmp;
};

这就导致我们在对这些不同的函数包装器进行保存的时候无法以统一的方式保存

例如:

假设一个函数模板的参数需要我们传入一个可调用对象,当我们传入不同的可调用对象时,这个函数也会被实例化多次,例如:

template<class F>
int tem_func(F f)
{
	static int count = 0;
	cout << "count=" << ++count << endl; //打印count的值
	cout << "&count=" << &count << endl; //打印count的地址
	return f();
}

int func()
{
	return 1;
}

class Func
{
public:
	int operator()()
	{
		return 1;
	}
};

auto lam_func = []()->int {return 1; };

可以看到,count是一个静态变量,但却有三个不同的地址,说明函数模板被实例化了三次

于是C++11引入了一种适配器,即function包装器,能够以统一的方式来保存这些可调用对象 

1.2 概念

std::function包装器是C++11引入的一个针对可调用对象的适配器,其本质是一个类模板

我们可以把包装器看作一个壳子,内部存放不同类型的可调用对象,这样对外看来它们都是一样的

要使用function包装器,首先得引入头文件<functional>

具体使用方法也并没有那么复杂,例如:

void swap(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

int main()
{
	function<void(int&, int&)> f1 = swap;
	return 0;
}

可以看到,我们只需要以这种方式传入可调用对象的返回值类型和参数类型,即可将一个可调用对象用包装器包装起来

不止是普通函数,其他可调用对象也是一样

void swap(int& a, int& b) //普通函数
{
	int tmp = a;
	a = b;
	b = tmp;
}

void(*swap_p)(int&, int&) = swap; //函数指针

class Swap
{
public:
	void operator()(int& a, int& b) //仿函数
	{
		int tmp = a;
		a = b;
		b = tmp;
	}
};

auto lam_swap = [](int& a, int& b)-> void { //Lambda表达式
	int tmp = a;
	a = b;
	b = tmp;
};

int main()
{
	function<void(int&, int&)> f1 = swap;
	function<void(int&, int&)> f2 = swap_p;
	function<void(int&, int&)> f3 = Swap();
	function<void(int&, int&)> f4 = lam_swap;
	return 0;
}

通过使用function包装器对可调用对象进行统一方式的包装,就可以避免前面的问题了:

1.3 应用场景

OJ链接:LCR 036. 逆波兰表达式求值 - 力扣(LeetCode)

在这道题的核心思想是使用栈,遍历tokens,遇到数字则入栈,遇到运算符则取出栈顶的两个数字进行运算,并将结果重新入栈

以前,我们需要通过switch语句判断符号的类型并运算,代码重复度高,不够优雅

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> st;
        for(auto &s : tokens)
        {
            if(s == "+" || s == "-" || s == "*" || s == "/")
            {
                int r = st.top();
                st.pop();
                int l = st.top();
                st.pop();
                cout << l << " " << r << endl;
                switch(s[0])
                {
                case '+':
                    st.push(l + r);
                    break;
                case '-':
                    st.push(l - r);
                    break;
                case '*':
                    st.push(l * r);
                    break;
                case '/':
                    st.push(l / r);
                    break;
                }
            }
            else
                st.push(stoi(s));
        }
        return st.top();
    }
};

现在我们就可以结合map、lambda表达式和function包装器重新做一下这道题了

代码如下:

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        map<string, function<int(int, int)>> cmd = {
            {"+", [](int a, int b)->int { return a + b; }},
            {"-", [](int a, int b)->int { return a - b; }},
            {"*", [](int a, int b)->int { return a * b; }},
            {"/", [](int a, int b)->int { return a / b; }}
        };
        stack<int> st;
        for(auto &s : tokens)
        {
            if(cmd.count(s))
            {
                int r = st.top();
                st.pop();
                int l = st.top();
                st.pop();
                st.push(cmd[s](l, r));
            }
            else
                st.push(stoi(s));
        }
        return st.top();
    }
};

需要注意的是,当我们使用包装器包装类成员函数的时候,需要加上类域并且取地址

对于非静态的类成员函数,除了其显式定义的参数外,还需要在参数中加上this指针,例如:


二、bind绑定器

std::bind是一个函数模板,可以接受一个可调用对象,并生成一个新的可调用对象

有人说了,这不是多此一举吗?实际上我们可以通过这种方式来完成修改原来的可调用对象参数的效果

例如我们想修改某个函数的顺序:

其中placeholders::_2代表原函数的第二个参数,placeholders::_1代表原函数的第一个参数

_n作为占位符代表了原来的可调用对象在bind之后生成的可调用对象的参数顺序

又例如我们想让某个参数是固定的值,就可以直接在bind中设置该参数的值

这种方式就能解决上面提到的function包装器包装非静态类成员函数时每次都要添加this指针的问题

例如:

完.

  • 36
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值