【C++】C++11-包装器

目录

1、function包装器

2、function包装器包装成员函数指针

[2.1 静态成员函数](#2.1 静态成员函数)

[2.2 非静态成员函数](#2.2 非静态成员函数)

3、bind包装器

[3.1 调整参数顺序](#3.1 调整参数顺序)

[3.2 调整参数个数](#3.2 调整参数个数)


1、function包装器

包装器是用来包装可调用对象的,这里的可调用对象主要有函数指针、仿函数、lambda表达式

function本质是一个类模板,也是一个包装器,头文件是<functional>

function<返回值(参数列表)> f = 可调用对象

int f(int a, int b)
{
	return a + b;
}
struct Functor
{
public:
	int operator() (int a, int b)
	{
		return a + b;
	}
};
int main()
{
	// 包装可调用对象
	function<int(int, int)> f1 = f;
	function<int(int, int)> f2 = Functor();
	function<int(int, int)> f3 = [](int a, int b) {return a + b; };
	cout << f1(1, 2) << endl;
	cout << f2(1, 2) << endl;
	cout << f3(1, 2) << endl;
	return 0;
}

优点是可以进行类型统一,就像我们玩游戏使用英雄释放技能时,按下一个技能的按键,英雄就会做出相应的动作,实际上,按下按键救会产生一个命令,命令通常是一个字符串,一般会将命令和函数进行映射。为了映射,此时可以使用一个map来完成映射,第一个参数存命令,第二个参数存这个命令对应的函数,但函数可能是函数指针、仿函数、lambda,lambda没有类型(底层有,但是同一个lambda在不同时间生成的类型都不一样),仿函数只能写死,函数指针可以,但太麻烦,所以就可以考虑使用包装器来进行统一

我们通过一个题目来看

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

这道题普通的做法是开一个栈,遍历数组,当遍历到的字符是数字时,就放入栈,当遍历到的字符不是数字时,就取栈顶的两个数来进行操作,并将操作完成的数再放入栈

class Solution {
public:
    bool isNumber(const string& s)
    {
        return !(s == "+" || s == "-" || s == "*" || s == "/");
    }
    int evalRPN(vector<string>& tokens) {
        stack<int> st;
        for(const auto& e : tokens)
        {
            if(isNumber(e)) st.push(stoi(e));
            else
            {
                int y = st.top();// 注意:这里是用x / y
                st.pop();
                int x = st.top();
                st.pop();
                switch(e[0])// 这里不能是e
                {
                    case '+':
                        st.push(x + y);
                        break;
                    case '-':
                        st.push(x - y);
                        break;
                    case '*':
                        st.push(x * y);
                        break;
                    case '/':
                        st.push(x / y);
                        break;
                }
            }
        }
        return st.top();
    }
};

现在可以使用一个map,第一个参数存放操作符,第二个参数存放对应的操作

class Solution {
public:
    bool isNumber(const string& s)
    {
        return !(s == "+" || s == "-" || s == "*" || s == "/");
    }
    int evalRPN(vector<string>& tokens) {
        stack<int> st;
        map<string, function<int(int, int)>> opFuncMap = {
            {"+", [](int x, int y){return x + y;}},
            {"-", [](int x, int y){return x - y;}},
            {"*", [](int x, int y){return x * y;}},
            {"/", [](int x, int y){return x / y;}}
        };
        for(const auto& e : tokens)
        {
            if(isNumber(e)) st.push(stoi(e));
            else
            {
                int y = st.top();// 注意:这里是用x / y
                st.pop();
                int x = st.top();
                st.pop();
                int ret = opFuncMap[e](x, y);
                st.push(ret);
            }
        }
        return st.top();
    }
};

2、function包装器包装成员函数指针

2.1 静态成员函数

class Plus
{
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)> f1 = Plus::plusi; // 要指定类域
	cout << f1(1, 1) << endl;
	return 0;
}

静态成员函数与普通函数是类似的,只是需要指定类域

2.2 非静态成员函数

int main()
{
	// 包装静态成员函数
	function<int(int, int)> f1 = Plus::plusi; // 要指定类域
	cout << f1(1, 1) << endl;

	// 包装非静态成员函数
	function<double(double, double)> f2 = Plus::plusd;
	return 0;
}

此时是错误的,非静态成员函数要取函数指针需要加&,静态成员函数可加可不加,最好也加上

int main()
{
	// 包装静态成员函数
	function<int(int, int)> f1 = &Plus::plusi; // 要指定类域
	cout << f1(1, 1) << endl;

	// 包装非静态成员函数
	function<double(double, double)> f2 = &Plus::plusd;
	return 0;
}

此时还是不行的,因为非静态成员函数还要隐藏的this指针,可以传指针,也可传对象(有名对象或匿名对象)

int main()
{
	// 包装静态成员函数
	function<int(int, int)> f1 = &Plus::plusi; // 要指定类域
	cout << f1(1, 1) << endl;

	// 包装非静态成员函数
	Plus ps;
	function<double(Plus*, double, double)> f2 = &Plus::plusd;
	cout << f2(&ps, 7.7, 7.7) << endl;

	// 包装非静态成员函数
	function<double(Plus, double, double)> f3 = &Plus::plusd;
	cout << f3(Plus(), 7.7, 7.7) << endl;
	return 0;
}

此时就会有一个疑问,非静态成员函数多出来的参数是this指针,是一个指针,为什么可以传对象呢?

实际上,function本质并不是将3个参数传给plusd,是拿到函数指针后,将函数指针作为成员变量存起来,我们传参时实际上是调用了function的operator(),operator()又去调用plusd,而调用plusd就需要用到Plus的对象或指针。也就是说,不是直接将3个参数传给plusd,并且this指针也不支持显示传参

3、bind包装器

bind是一个函数模板,主要用于对可调用对象调整参数的顺序、个数,返回一个仿函数,直接使用auto接收

placeholders是一个命名空间,里面有很多标识符(_1,_2,_3,...),可用这些标识符去代表对应的参数,_1始终代表第一个实参,_2代表第二个实参

3.1 调整参数顺序

using placeholders::_1;
using placeholders::_2;
using placeholders::_3;
int Sub(int x, int y)
{
	return (x - y) * 10;
}
int main()
{
	// 调整参数顺序
	auto sub1 = bind(Sub, _1, _2);
	cout << sub1(10, 5) << endl;
	auto sub2 = bind(Sub, _2, _1);
	cout << sub2(10, 5) << endl;
	return 0;
}

结果是50 -50。_1代表的始终是第1个实参,无论是sub1(10, 5),还是sub2(10, 5),第1个实参都是10,都会匹配到_1,但是sub1和sub2中_1处在的位置是不同的,sub1会将_1当成第1个形参来传,sub2会将_1当成是第2个形参来传,也就是说在调用函数时,是按顺序传的

3.2 调整参数个数

int main()
{
	// 调整参数顺序
	auto sub1 = bind(Sub, _1, _2);
	cout << sub1(10, 5) << endl;
	auto sub2 = bind(Sub, _2, _1);
	cout << sub2(10, 5) << endl;

	// 调整参数个数
	auto sub3 = bind(Sub, 100, _1);
	cout << sub3(5) << endl;
	auto sub4 = bind(Sub, _1, 100);
	cout << sub4(5) << endl;
	return 0;
}

结果是950 -950

实际上,bind返回的是一个仿函数对象,也就是说上面的sub1、sub2、sub3、sub4都是仿函数对象,本质也是调用operator(),只是里面调用的顺序不同

其实,bind主要用于绑定一些固定参数,向上面非静态成员函数的传参每次都需要传指针或对象,可用直接绑定了,这样只需要传操作数

class Plus
{
public:
	static int plusi(int a, int b)
	{
		return a + b;
	}
	double plusd(double a, double b)
	{
		return a + b;
	}
};
int main()
{
	function<double(double, double)> f1 = bind(&Plus::plusd, Plus(), _1, _2);
	cout << f1(7.7, 7.7) << endl;
	return 0;
}
相关推荐
一点媛艺2 小时前
Kotlin函数由易到难
开发语言·python·kotlin
姑苏风2 小时前
《Kotlin实战》-附录
android·开发语言·kotlin
奋斗的小花生3 小时前
c++ 多态性
开发语言·c++
魔道不误砍柴功3 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
闲晨3 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
老猿讲编程3 小时前
一个例子来说明Ada语言的实时性支持
开发语言·ada
UestcXiye4 小时前
《TCP/IP网络编程》学习笔记 | Chapter 3:地址族与数据序列
c++·计算机网络·ip·tcp
Chrikk4 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*4 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue4 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang