[C++11] 包装器

66 阅读4分钟

functionbind 是C++11标准从boost库引入到std中的。

1. function 包装器

function是一种函数包装器,也叫做适配器。它可以对可调用对象进行包装,C++中的function本质就是一个类模板。

函数原型:

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

参数:

  • Ret : 调用函数返回值的类型
  • Args :任意数量和类型的模板参数包

1.1 包装示例

function包装器可以对可调用对象进行包装,包括函数指针(函数名)、仿函数(函数对象)、lambda表达式、类的成员函数。比如:


#include <iostream>
#include <functional>

//function包装器可以对可调用对象进行包装,包括函数指针(函数名)、仿函数(函数对象)、lambda表达式、类的成员函数

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

//仿函数
struct func2
{
	int operator()(int x,int y)
	{
		return x + y;
	}
};

struct MyStruct
{
	int m_value;
	int func3(int x)
	{
		return x + m_value;
	}
};

int main()
{
	//包装函数
	std::function<int(int, int)> fn1 = func1;
	//包装函数指针
	std::function<int(int, int)> fn2 = &func1;
	//包装仿函数
	std::function<int(int, int)> fn3 = func2();
	
	//包装lambda
	std::function<int(int, int)> fn4 = [](int x, int y) {
			return x + y;
	};

	//包装类的成员函数
	std::function<int(MyStruct&,int)> fn5 = &MyStruct::func3;
	//包装类的成员变量
	std::function<int(MyStruct&)> fn6 = &MyStruct::m_value;


	std::cout << fn1(1, 2) << std::endl;
	std::cout << fn2(1, 2) << std::endl;
	std::cout << fn3(1, 2) << std::endl;
	std::cout << fn4(1, 2) << std::endl;

	MyStruct ms{1};

	std::cout << fn5(ms,2) << std::endl;
	std::cout << fn6(ms) << std::endl;

	return 0;
}
  • 包装时指明返回值类型和各形参类型,然后将可调用对象赋值给function包装器即可,包装后function对象就可以像普通函数一样使用了。
  • 包装非静态的成员函数时需要注意,非静态成员函数的第一个参数是隐藏this指针,因此在包装时需要指明第一个形参的类型为类的类型。

1.2 function 统一类型

#include <iostream>
#include <functional>

template<class F, class T>
T test1(F func, T t)
{
	static int value = 0;
	std::cout << "valude =" << ++value << std::endl;
	std::cout << "&valude =" << &value << std::endl;

	return func(t);
}

int test2(int x)
{
	return x;
}
struct Test3
{
	int operator()(int x)
	{
		return x;
	}
};


int main()
{
	std::cout << test1(test2, 6) << std::endl;
	std::cout << test1(Test3(), 66) << std::endl;
	std::cout << test1([](int x)->int {return x; }, 666) << std::endl;

}

运行结果: image.png

由于函数指针、仿函数、lambda表达式是不同的类型,因此test1函数会被实例化出三份,三次调用test1函数所打印value的地址也是不同的。

这时就可以用function包装器分别对这三个可调用对象进行包装然后再用这三个包装后的可调用对象来调用test1函数, 如下:

#include <iostream>
#include <functional>

template<class F, class T>
T test1(F func, T t)
{
	static int value = 0;
	std::cout << "valude =" << ++value << std::endl;
	std::cout << "&valude =" << &value << std::endl;

	return func(t);
}

int test2(int x)
{
	return x;
}
struct Test3
{
	int operator()(int x)
	{
		return x;
	}
};


int main()
{
	std::function<int(int)> fn1 = test2;
	std::function<int(int)> fn2 = Test3();
	std::function<int(int)> fn3 = [](int x)->int {return x;  };

	std::cout << test1(fn1, 6) << std::endl;
	std::cout << test1(fn2, 66) << std::endl;
	std::cout << test1(fn3, 666) << std::endl;
}

运行结果:

image.png

这时就只会实例化出一份test1函数。

1.3 function包装器的意义

  • 将可调用对象的类型进行统一,便于我们对其进行统一化管理。
  • 包装后明确了可调用对象的返回值和形参类型,更加方便使用者使用。

2. bind

bind和function都是包装器,bind的用途是绑定固定的参数,在调用时可以不用传递某些参数。
函数原型:

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

参数:

  • fn: 函数对象、函数指针或类的成员函数。
  • args: 要绑定的参数列表:值或占位符
    • 占位符就是占着位置,不起实际作用,用的时候传递参数即可

2.1 使用示例

绑定普通函数


#include <iostream>
#include <functional>
double func(double x, double y)
{
	return x / y;
}

int main() 
{
	//bind函数	
	auto fn1 = std::bind(func, 10, 2);
	std::cout << fn1() << std::endl;

	//带占位符
	auto fn2 = std::bind(func, std::placeholders::_1, 2);   //return x/2
	std::cout << fn2(10) << std::endl;						// 5

	auto fn3 = std::bind(func, std::placeholders::_1, std::placeholders::_2); // return x / y
	std::cout << fn3(10,2) << std::endl;									 // 5

	auto fn4 = std::bind(func, std::placeholders::_2, std::placeholders::_1); //return y / x
	std::cout << fn4(10,2) << std::endl;									  //0.2

	//带返回值类型
	auto fn5 = std::bind<int>(func, std::placeholders::_2, std::placeholders::_1); //return (int)(y/x)
	std::cout << fn5(10, 2) << std::endl;										  // 0

}

绑定类的成员对象

#include <iostream>
#include <functional>

class Calculator {
public:
	int add_sum(int a, int b)  
	{
		return a + b;
	}
};

int main() {
	Calculator calcu;
	Calculator& ref_calcu = calcu;
	Calculator* prt_calcu = &calcu;
        
	std::function<int(int)> func1 = std::bind<int>(&Calculator::add_sum, calcu, std::placeholders::_1, 20); //return Calculator().add_sum(x,20);/
	std::function<int(int)> func2 = std::bind<int>(&Calculator::add_sum, ref_calcu, std::placeholders::_1, 20); //return Calculator().add_sum(x,20);
	std::function<int(int)> func3 = std::bind<int>(&Calculator::add_sum, prt_calcu, std::placeholders::_1, 20); //return Calculator().add_sum(x,20)
	std::cout << func1(10) << std::endl; //30
	std::cout << func2(10) << std::endl; //30
	std::cout << func3(10) << std::endl; //30

	return 0;
}

绑定成员函数时,需要传递对象或对象指针或引用