为什么要有 包装器function?存在的意义在哪?

为什么要有 包装器function?存在的意义在哪?

篇一、

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

function类模板的原型如下:

template function; // undefined

template

class function;

模板参数说明: Ret: 被调用函数的返回类型 Args…:被调用函数的形参

function包装器可以对可调用对象进行包装,包括函数指针(函数名)、仿函数(函数对象)、lambda表达式、类的成员函数。 原文链接:https://blog.csdn.net/weixin_74461263/article/details/143452009

二、为什么要有 包装器function?有什么意义?

2.1 C++中需要包装器function的主要原因:是为了解决不同可调用对象类型不一致的问题,从而减少模板的实例化次数,从而提高代码的通用性和效率。‌

在C++中,函数指针、仿函数和lambda表达式等都是可调用的类型,但它们在模板中的使用会导致编译时多次实例化,从而影响效率。例如,如果有一个模板函数接收不同类型的可调用对象,编译器会为每种类型的可调用对象生成不同的实例化代码,这会导致大量的重复代码生成,降低效率。为了解决这个问题,C++11引入了std::function包装器,它可以将不同类型的可调用对象统一封装为相同的类型,从而减少模板的实例化次数,提高代码的效率和重用性‌。

2.2 std::function的基本概念和用法

std::function是一个类模板,定义在头文件中。它的主要作用是将不同类型的可调用对象(如函数指针、仿函数、lambda表达式等)封装为统一的类型。使用std::function可以避免因为可调用对象类型不同而导致的模板多次实例化问题。

2.3 举例说明:

2.3.1 未使用std::function()包装器之前:

#include "stdafx.h"

#include

#include

using namespace std;

// 函数模板会被实例化多次

template

T useF(F f, T x) //实例化的时候,不会调用useF()函数。只有在调用的时候才会执行useF()函数

{

static int icount = 0;

cout << "count:" << ++icount << endl;

cout << "count:" << &icount << endl;

return f(x);

}

double func(double i)

{

return i / 2;

}

struct Functor

{

double operator()(double d)

{

return d / 3;

}

};

int main()

{

// 函数名

cout << useF(func, 11.11) << endl;

cout << "==========================" << endl;

// 函数对象

cout << useF(Functor(), 11.11) << endl;

cout << "==========================" << endl;

// lamber表达式

cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;

system("pause");

return 0;

}

结果:

在 main 函数中使用了3个不同的函数对象(函数名、函数对象和 lambda 表达式),每个都调用了 useF 函数, 实例化了三份useF函数 (比方说:useF1、useF2、useF3三份函数 ),每个不同的函数对象(函数名、函数对象和 lambda 表达式)调用自己实例化的useF函数。因此count值不会增加,还是1;

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

但实际这里根本没有必要实例化出三份useF函数,因为三次调用useF函数时传入的可调用对象虽然是不同类型的,但这三个可调用对象的返回值和形参类型都是相同的。

这时就可以用包装器分别对着三个可调用对象进行包装,然后再用这三个包装后的可调用对象来调用useF函数,这时就只会实例化出一份useF函数。

2.3.2 使用包装器std::function()代码如下:

int main()

{

// 函数名 生成一个函数包装器,f1就是函数指针 == double (*f1)(double)

std::function f1 = func;

cout << useF(f1, 11.11) << endl;

cout << "==========================" << endl;

// 函数对象

std::function f2 = Functor();

cout << useF(f2, 11.11) << endl;

cout << "==========================" << endl;

// lamber表达式

std::function f3 = [](double d)->double { return d / 4; };

cout << useF(f3, 11.11) << endl;

system("pause");

return 0;

}

结果:

包装后,这三个可调用对象都是相同的function类型,因此最终只会实例化出一份useF函数,该函数的第一个模板参数的类型就是function类型的。

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

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

三、function包装器的应用

3.1 调用普通函数

#include

#include

int f(int a, int b)

{

return a+b;

}

int main()

{

std::functionfunc = f;

cout<

system("pause");

return 0;

}

3.2 调用函数对象

#include

using namespace std;

//function object

struct functor

{

public:

int operator() (int a, int b)

{

return a + b;

}

};

int main()

{

functor ft;

function func = ft();

cout<

return 0;

}

3.3 调用模板函数对象

#include

using namespace std;

//function object

template

struct functor

{

public:

T operator() (T a, T b)

{

return a + b;

}

};

int main()

{

functor ft;

function func = ft();

cout<

return 0;

}

3.4 调用lambda表达式

#include

#include

using namespace std;

int main()

{

auto f = [](const int a, const int b) {return a + b; };

std::functionfunc = f;

cout << func(1, 2) << endl; // 3

system("pause");

return 0;

}

3.5 调用类静态成员函数

#include

#include

using namespace std;

class Plus

{

public:

static int plus(int a, int b)

{

return a + b;

}

};

int main()

{

function f = &Plus::plus;

cout << f(1, 2) << endl; //3

system("pause");

return 0;

}

3.6 调用类成员函数

#include

#include

using namespace std;

class Plus

{

public:

int plus(int a, int b)

{

return a + b;

}

};

int main()

{

Plus p;

function f = &Plus::plus;

//function f = &Plus::plus;

cout << f(p,1, 2) << endl; //3

system("pause");

return 0;

}

注意:在使用function包装类的成员函数时需要注意,类的非静态成员函数参数中天然包含了一个该类指针参数,即this指针。所以我们包装时需要在function参数列表中,也加上该类指针或者该类,实例化时也同理。

3.7 调用类公有数据成员

#include

#include

using namespace std;

class Plus

{

Plus(int num_):num(num_){}

public:

int plus(int a, int b)

{

return a + b;

}

int num;

};

int main()

{

const Plus p(2);

function f = &Plus::num;

//function f = &Plus::num;

cout << f(p) << endl; //2

system("pause");

return 0;

}

3.8 通过bind函数调用类成员函数

#include

#include

using namespace std;

class Plus

{

public:

int plus(int a, int b)

{

return a + b;

}

};

int main()

{

Plus p;

// 指针形式调用成员函数

function f = bind(&Plus::plus, &p, placeholders::_1, placeholders::_2);// placeholders::_1是占位符

// 对象形式调用成员函数

function f1 = bind(&Plus::plus, p, placeholders::_1, placeholders::_2);// placeholders::_1是占位符

cout << f(1, 2) << endl; //3

cout << f1(1, 2) << endl; //3

system("pause");

return 0;

}

四、function函数在“逆波兰表达式”中的应用:

逆波兰表达式(利用map+function来解决)

分析:我们原本用栈来完成,现在我们可以用map+function来解决改进后,用function把lambda表达式包装起来了lambda相关博客传送门:【C++11特性篇】lambda表达式玩法全解

原文链接:https://blog.csdn.net/qq_35721743/article/details/83217416

原文链接:https://blog.csdn.net/weixin_74461263/article/details/143452009

C++11 【 function包装器,bind包装器】-CSDN博客

篇二、C++11 std::function函数包装器 - kaizenly - 博客园

【1】std::function简介

std::function是一个函数包装器模板,最早来自boost库,对应其boost::function函数包装器。

一个std::function类型对象实例可包装以下可调用元素类型如下:

(1)函数

(2)函数指针

(3)类成员函数指针

(4)任意类型的函数对象(例如:定义了operator()操作符重载的类型)。

std::function对象可被拷贝和转移,并且可以使用指定的调用特征来直接调用目标元素。

当std::function对象未包裹任何实际的可调用元素,调用该std::function对象将抛出std::bad_function_call异常。

【2】std::funciton使用

#include

#include

using namespace std;

int subtract(int m, int n)

{

return (m - n);

}

template < class T>

T g_sub(T m, T n)

{

return (m - n);

}

auto g_Lambda = [](int m, int n)

{

return (m - n);

}; // 注意:匿名函数此处有分号

struct Sub

{

int operator()(int m, int n)

{

return (m - n);

}

};

template < class T>

struct SubTemp

{

T operator()(T m, T n)

{

return (m - n);

}

};

class SubOper

{

public:

static int st_sub(int m, int n)

{

return (m - n);

}

template < class T>

static T temp_sub(T m, T n)

{

return (m - n);

}

double result(double m, double n)

{

return (m - n);

}

double const_result(double m, double n) const

{

return (m - n);

}

};

int main()

{

// 旧式写法

typedef int (*pFunc) (int, int);

pFunc oldFunc = subtract;

cout << "Test old style :: " << (*oldFunc)(9, 10) << endl; // -1

// [0] 包装函数指针对象

std::function from_pFunc = oldFunc;

cout << "Test0 :: " << from_pFunc(10, 10) << endl; // 0

// [1]包装普通函数

std::function newFunc = subtract;

cout << "Test1 :: " << newFunc(11, 10) << endl; // 1

// [2]包装模板函数

std::function tempFunc = g_sub;

cout << "Test2 :: " << tempFunc(12, 10) << endl; // 2

// [3]包装Lambda函数

std::function lambdaFunc = g_Lambda;

cout << "Test3 :: " << lambdaFunc(13, 10) << endl; // 3

// [4]包装仿函数

std::function objFunc = Sub();

cout << "Test4 :: " << objFunc(14, 10) << endl; // 4

// [5]包装模板函数对象

std::function tempFuncObj = SubTemp();

cout << "Test5 :: " << tempFuncObj(15, 10) << endl; // 5

// [6] 类静态函数

std::function stFunc = &SubOper::st_sub;

cout << "Test6 :: " << stFunc(16, 10) << endl; // 6

// [7] 类静态模板函数

std::function tempSTFunc = &SubOper::temp_sub;

cout << "Test7 :: " << tempSTFunc(17, 10) << endl; // 7

// [8] 类普通函数(普通函数绑定需要依赖类对象)

SubOper subOperObject;

// [8.1] 使用bind,将类对象地址绑定上

std::function resultFunc = std::bind(&SubOper::result, &subOperObject, placeholders::_1, placeholders::_2);

cout << "Test8.1 :: " << resultFunc(18.2, 10.1) << endl; // 8.1

// [8.2] 不使用bind

std::function resultFunc2 = &SubOper::result;

cout << "Test8.2 :: " << resultFunc2(subOperObject, 18.3, 10.1) << endl; // 8.2

// [8.3] const

std::function const_resultFunc2 = &SubOper::const_result;

cout << "Test8.3 :: " << const_resultFunc2(subOperObject, 18.4, 10.1) << endl; // 8.3

// [8.4] 常量对象

const SubOper subOperConst;

std::function const_Func2 = &SubOper::const_result;

cout << "Test8.4 :: " << const_Func2(subOperConst, 18.5, 10.1) << endl; // 8.4

// [9] 应用示例(为了解耦)

class TestA

{

public:

bool destoryByName(const std::string & name)

{

return doDestoryByName(name);

}

public:

std::function destory_handler;

private:

bool doDestoryByName(std::string name)

{

return destory_handler(name);

}

};

class TestB

{

public:

bool destory(const std::string & name)

{

cout << "Test9 :: Call TestB destory | name : " << name << endl;

return true;

};

};

TestB objB;

TestA objA;

objA.destory_handler = [&](const std::string& name)->bool {

// 摧毁操作

return objB.destory(name);

};

objA.destoryByName("kaizen");

// [10] 为空时运行时异常

std::function dealWithFunc;

try

{

if (nullptr == dealWithFunc)

{

throw runtime_error("std::bad_function_call"); //抛出异常

}

else

{

dealWithFunc(100, 10);

}

}

catch (exception e)

{

cout << "Test10 :: " << e.what() << endl; // 捕获异常,然后程序结束

}

// [11] 作为形参类型使用

class Print

{

public:

void doPrint(std::string content)

{

cout << "Test11 :: Call Print Class doPrint : " << content << endl;

}

};

class Person

{

public:

Person(std::string name = {}) : m_name(name)

{ }

void show(std::function printFunc)

{

printFunc(m_name);

}

private:

std::string m_name;

};

// error

Person oObj1{ "hello" };

//p1.show(&Print::doPrint); // 无法将参数 1 从“void (__cdecl main::Print::* )(std::string)”转换为“std::function

// 方式一:

std::function printFunc1 = [](std::string str) {

Print{}.doPrint(str);

};

Person{"C++"}.show(printFunc1);

// 方式二:

Print oPrint;

std::function printFunc2 = std::bind(&Print::doPrint, &oPrint, placeholders::_1);

Person{"Python"}.show(printFunc2);

system("pause");

}

结果:

相关推荐

摩托手套(★) | 嘭! (久经沙场)
365官方登录入口

摩托手套(★) | 嘭! (久经沙场)

📅 08-31 👁️ 2664