【C++11新特性】function包装器

文章目录

  • 1. function包装器
    • 1.1 function包装器介绍
    • 1.2 function包装器对类型的统一
    • 1.3 function包装器的意义

1. function包装器

1.1 function包装器介绍

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

template <class T> function;
template <class Ret, class... Args>
class Function<Ret(args...)>;
  • Ret :被包装的可调用对象的返回值类型。
  • Args… :被包装的课调用对象的形参类型。

包装示例

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

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

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

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

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

int main()
{
	// 1、包装函数指针(函数名)
	function<int(int, int)> func1 = f;
	cout << func1(1, 2) << endl;

	// 2、包装仿函数(函数对象)
	function<int(int, int)> func2 = Functor();
	cout << func2(1, 2) << endl;

	// 3、包装lambda表达式
	function<int(int, int)> func3 = [](int a, int b) {return a + b; };
	cout << func3(1, 2) << endl;

	// 4、包装静态成员函数
	function<int(int, int)> func4 = &Plus::plusi; // &可省略
	cout << func4(1, 2) << endl;

	// 5、包装类的非静态成员函数
	function<double(Plus, double, double)> func5 = &Plus::plusd; // &不可省略
	cout << func5(Plus(), 1.1, 2.2) << endl;

	return 0;
}


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

1.2 function包装器对类型的统一

对于以下函数模板useF:

  • 传入该函数模板的第一个参数可以是任意的,比如函数指针,仿函数,lambda表达式等等。
  • useF中定义了静态变量count,并在每次调用时将count的值和地址进行了打印,可判断多次调用时调用的是否是同一个useF函数。

1.3 function包装器的意义

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

【C++11新特性】function包装器_第1张图片
在传入第二个参数类型相同的情况下,如果传入的可调用对象的类型是不同的,那么在编译阶段该函数模板就会被实例化多次。

template <class F, class T>
T useF(F f, T x)
{
	static int count = 0;
	cout << "count: " << ++count << endl;
	cout << "count: " << &count << endl;

	return f(x);
}

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

struct Functor
{
	double operator()(double d)
	{
		return d / 3;
	}
};

int main()
{
	// 函数指针
	cout << useF(f, 11.11) << endl;
	// 仿函数
	cout << useF(Functor(), 11.11) << endl;
	// lambda表达式
	cout << useF([](double d)->double {return d / 4; }, 11.11) << endl;

	return 0;
}

运行结果如下:

【C++11新特性】function包装器_第2张图片

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

  • 但实际这里根本没有必要实例化出三份useF函数,因为三次调用时传入的可调用对象虽然是不同类型的,但这三个可调用对象的返回值和形参类型都是相同的。
  • 这时就可以用包装器对这三个可调用对象进行包装,然后再用这三个包装后的可调用对象来调用useF函数,这时就只会实例化出一份useF函数。
  • 根本原因就是因为包装后,这三个可调用对象都是相同的function类型,因此最终只会实例化出一份useF函数,该函数的第一个模板参数的类型就是function类型的。

【C++11新特性】function包装器_第3张图片

运行结果如下:

【C++11新特性】function包装器_第4张图片

这时三次调用useF函数所打印count的地址就是相同的,并且count在三次调用后会被累加到3,表示这一个useF函数被调用了三次。

你可能感兴趣的:(C++,c++,开发语言)