C++11之包装器(function)

发布于:2022-12-02 ⋅ 阅读:(170) ⋅ 点赞:(0)

🌈前言

本篇文章进行C++11中包装器的学习!!!


🚁1、包装器

🚂1.1、function包装器

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

Test = func(x);
  • 上面func可能是什么呢?

  • func可能是一个函数名?函数指针?函数对象(仿函数对象)?还是一个lamber表达式对象?

  • 这些都是可调用的类型!如此丰富的类型,如果传参给模板,可能会导致模板的效率低下!

namespace FC
{
	// 包装器章节:一个类模板,可能同时实例化多份相同的代码,不同的类型调用同样版本的函数,导致效率低下
	// 这个类型可能是函数指针,仿函数,lambda表达式等等....
	template <typename F, typename T>
	T useF(F f, T t)
	{
		static int Count = 0;
		cout << "Count: " << ++Count << endl;
		cout << "Count address: " << &Count << endl;

		return f(t);
	}

	double f(double x)
	{
		return x / 2;
	}

	struct Func
	{
		double operator()(double x)
		{
			return x / 3;
		}
	};

	void Test()
	{
		// 函数指针
		cout << useF(f, 1.2) << endl;

		// 仿函数(函数对象)
		cout << useF(Func(), 1.2) << endl;

		// lambda表达式
		cout << useF([](double x)->double { return x / 4; }, 1.2);
	}
}

在这里插入图片描述

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


包装器可以很好的解决上面的问题:
https://legacy.cplusplus.com/reference/functional/function/?kw=function

// 类模板原型如下
template <class T> function; // undefined

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

模板参数说明:

  • std::function类模板是在functional头文件中定义的

  • Ret:被调用函数的返回类型

  • Args…:被调用函数的参数列表

// 使用方法如下:
#include <functional>
int f(int a, int b)
{
	return a + b;
}

struct Functor
{
public:
	int operator() (int a, int b)
	{
		return a + b;
	}
};

int main()
{
	// 函数名(函数指针)
	std::function<int(int, int)> func1 = f;
	cout << func1(1, 2) << endl;
	
	// 函数对象
	std::function<int(int, int)> func2 = Functor();
	cout << func2(1, 2) << endl;
	
	// lamber表达式
	std::function<int(int, int)> func3 = [](const int a, const int b)
	{
		return a + b; 
	};
	cout << func3(1, 2) << endl;
}

在这里插入图片描述


还能对类静态成员函数和非静态成员函数进行包装:

class T
{
public:
	static int Ti(int x, int y)
	{
		return x + y;
	}

	int Td(int x, int y)
	{
		return x - y;
	}
};
	
void Test()
{
	// &T::Ti/T().Ti -- 静态成员函数,不包含隐藏this指针
	std::function<int(int, int)> f4 = T().Ti; 
	cout << f4(1, 2) << endl;
	
	// 成员函数包含隐藏this指针,形参需要多定义一个类型
	std::function<int(T, int, int)> f5 = &T::Td; 
	cout << f5(T(), 10, 5) << endl;
}

在这里插入图片描述

注意:

  • 对类中的静态成员进行包装时,可以不对其函数名取地址(T::Ti),也可以直接匿名调用赋值(T().Ti)

  • 但是对类中成员函数进行包装时,因为成员函数中都隐藏着一个this指针,所以我们在定义参数时,需要显示传一个类,否则会报错

  • 对成员函数进行包装,右值只能是取地址成员函数名,可能是只能通过它去调用成员函数


有了包装器,如何解决模板的效率低下,实例化多份的问题呢?

namespace FC
{
	// 包装器章节:一个类模板,可能同时实例化多份相同的代码,不同的类型调用同样版本的函数,导致效率低下
	// 这个类型可能是函数指针,仿函数,lambda表达式等等....
	template <typename F, typename T>
	T useF(F f, T t)
	{
		static int Count = 0;
		cout << "Count: " << ++Count << endl;
		cout << "Count address: " << &Count << endl;

		return f(t);
	}

	double f(double x)
	{
		return x / 2;
	}

	struct Func
	{
		double operator()(double x)
		{
			return x / 3;
		}
	};

	void Test()
	{
		std::function<double(double)> f1 = f;
		cout << useF(f1, 1.2) << endl;

		std::function<double(double)> f2 = Func();
		cout << useF(f2, 1.2) << endl;

		std::function<double(double)> f3 = [](double x)->double { return x / 4; };
		cout << useF(f3, 1.2) << endl;
	}
}

在这里插入图片描述


包装器的其他一些场景:

https://leetcode.cn/problems/evaluate-reverse-polish-notation/submissions/

// 使用包装器以后的玩法
class Solution {
public:
	int evalRPN(vector<string>& tokens) {
		stack<int> st;
		map<string, function<int(int, int)>> opFuncMap =
		{
		{ "+", [](int i, int j) {return i + j; } },
		{ "-", [](int i, int j) {return i - j; } },
		{ "*", [](int i, int j) {return i * j; } },
		{ "/", [](int i, int j) {return i / j; } }
		};
		for (auto& str : tokens)
		{
			if (opFuncMap.find(str) != opFuncMap.end())
			{
				int right = st.top();
				st.pop();
				int left = st.top();
				st.pop();
				st.push(opFuncMap[str](left, right));
			}
			else
			{
				// 1、atoi itoa
				// 2、sprintf scanf
				// 3、stoi to_string C++11
				st.push(stoi(str));
			}
		}
		return st.top();
	}
};

🚃2、bind

  • std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表

  • 一般而言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M可以大于N,但这么做没什么意义)参数的新函数

  • 同时,使用std::bind函数还可以实现参数顺序调整等操作

// 原型如下:
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的一般形式: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为第二个参数,以此类推

  • bind还能对原对象的参数列表进行调换顺序,例如:_2为newCallable的第一个参数,_1为第二个参数,那么在传参时就必须反着来传了

// 使用举例
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;
}

还能这样使用:

namespace Bind
{
	// 包装器章节:一个类模板,可能同时实例化多份相同的代码,不同的类型调用同样版本的函数,导致效率低下
	// 这个类型可能是函数指针,仿函数,lambda表达式,类成员函数等等....
	int f(int a, int b)
	{
		return a + b;
	}

	struct Functor
	{
	public:
		int operator() (int a, int b)
		{
			return a + b;
		}
	};

	class T
	{
	public:
		int Ti(int x, int y)
		{
			return x + y;
		}
	};

	void Test()
	{
		map<string, std::function<int(int, int)>> m
		{
			{ "函数指针", f },
			{ "仿函数", Functor() },
			{ "成员函数", std::bind(&T::Ti, T(), placeholders::_1, placeholders::_2) }
		};

		// []返回pair<string, function>中的second(function本身),可以直接通过[]找到对应的key对其进行调用
		cout << m["函数指针"](1, 2) << endl;
		cout << m["仿函数"](10, 20) << endl;
		cout << m["成员函数"](100, 200) << endl;
	}
}

在这里插入图片描述

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到