首页 > 编程语言 >c++function bind包装器详解

c++function bind包装器详解

时间:2024-08-01 12:26:11浏览次数:24  
标签:function count return int bind c++ 参数

包装器

function包装器

  • function包装器也叫适配器,c++中的function本质是一个类模板,也是一个包装器,那么为什么需要包装器呢。
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;
    // lamber表达式

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

运行结果:

candy:~/code/cpp/test/tuple $  ./a.out 
count:1
count:0x5624be36c15c
5.555
count:1
count:0x5624be36c160
3.70333
count:1
count:0x5624be36c158
2.7775

​ 对于一个ret = func(x),我们并不知道func是什么,func可能是函数指针,也可能是函数名,仿函数,lambda表达式,所以这些都是可调用的类型,这样就会导致模板的效率低下,就如同我刚写的代码,count的地址是不一样的,这就是说函数模板实例化出来了多份,而包装器就可以很好的解决这个问题。

function说明

std::function在头文件<functional>

// 类模板原型如下
template <class T> function;     // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
模板参数说明:

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

function的使用

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(){
	//绑定普通函数
	function<int(int, int)> f1 = f;
	cout << f1(2,3) << endl;
	//绑定仿函数
	function<int(int,int)> f2 = Functor();
	cout << f2(3,4) << endl;
	//绑定lambda表达式
	function<int(int,int)> f5 = [](int a, int b){ return a+b; };
	cout << f5(323,3243) << endl;
	//绑定类静态成员函数  -> 要指定类域
	function<int(int,int)> f3 = Plus::plusi;
	cout << f3(32,32) << endl;
	//绑定非静态成员函数:
	//1.首先非静态成员函数的函数指针要加上&符号
	//2.因为非静态成员函数中还有一个隐藏的参数 -> this指针,所以要显示的传递一个类的对象或者指针
	function<double(Plus,double,double)> f4 = &Plus::plusd;
	Plus p1;
	cout << f4(p1, 1.1,1.32) << endl;
	return 0;
}

​ 这里除了非静态成员变量的使用有两点要注意以外,其余的使用都比较简单,那么为什么function可以解决模板低效的问题呢 -> function 完成了对类型的统一

#include <functional>

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()
{
	// 函数名
    
 	std::function<double(double)> func1 = f;
 	cout << useF(func1, 11.11) << endl;
 	// 函数对象

 	std::function<double(double)> func2 = Functor();
 	cout << useF(func2, 11.11) << endl;
 	// lamber表达式

 	std::function<double(double)> func3 = [](double d)->double{ return d / 4; };
 	cout << useF(func3, 11.11) << endl;
 	return 0;
}

运行结果:

candy:~/code/cpp/test/tuple $  g++ test.cc 
candy:~/code/cpp/test/tuple $  ./a.out 
count:1
count:0x564b0f7cb154
5.555
count:2
count:0x564b0f7cb154
3.70333
count:3
count:0x564b0f7cb154
2.7775

​ 可以看到这里的count的地址都是相同的,这就统一了类型的问题,而function的好处还不止这些,试想一下如果要存储对应的string和函数的关系,我们要如何存储,在c++11之前或许需要使用函数指针来进行操作,但是函数指针相对来说阅读起来观感并不好,但是现在一个function就可以解决这个问题。

请添加图片描述

对于这样的一个题,我们就可以使用function来统一类型使用map来进行管理

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> v;
        unordered_map<string, function<int(int,int)>> op = {
            {"+", [](int a, int b){ return a + b;}},
            {"-", [](int a, int b){ return a - b;}},
            {"*", [](int a, int b){ return a * b;}},
            {"/", [](int a, int b){ return a / b;}}
        };
        for(auto e : tokens){
            if(op.count(e)){
                int right = v.top();
                v.pop();
                int left = v.top();
                v.pop();
                v.push(op[e](left, right));
            }
            else{
                v.push(stoi(e));
            }
        }
        return v.top();
    }
};

bind

  • std::bind定义在functional头文件中,是一个函数模板,他就是一个函数包装器(适配器),接收一个可调用对象(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为第二个参数,以此类推。

eg:

// 使用举例

#include <functional>

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;
}

  • 这里除了可以这样让非静态成员变量少传一个参数之外,还可以绑定一写必须要传的参数从而减少参数个数。

标签:function,count,return,int,bind,c++,参数
From: https://blog.csdn.net/xiyun_king/article/details/140838807

相关文章

  • 三种语言实现计算二进制中1的个数(C++/Python/Java)
    题目给定一个长度为n的数列,请你求出数列中每个数的二进制表示中1的个数。输入格式第一行包含整数n。第二行包含n个整数,表示整个数列。输出格式共一行,包含n个整数,其中的第i个数表示数列中的第i个数的二进制表示中1的个数。数据范围1≤n≤100000,0≤数列中......
  • 三种语言实现双指针判断子序列(C++/Python/Java)
    题目给定一个长度为n的整数序列a1,a2,…,an以及一个长度为m的整数序列b1,b2,…,bm。请你判断a序列是否为b序列的子序列。子序列指序列的一部分项按原有次序排列而得的序列,例如序列{a1,a3,a5}是序列{a1,a2,a3,a4,a5}的一个子序列。输入格式第一行包含两个整数......
  • C++竞赛初阶L1-05-第四单元-判断语句(第19课)100003: 最大数输出
    题目内容输入三个整数,输出最大的数。输入格式输入为一行,包含三个整数,数与数之间以一个空格分开。输出格式输出一行,包含一个整数,即最大的整数。样例1输入102056样例1输出56程序代码输出:#include<bits/stdc++.h>usingnamespacestd;intmain(){ inta,b,c......
  • C++程序中的类型转换与进程异常退出血案复盘
    在C++编程中,类型转换是一个常见的操作,它允许程序员将一个数据类型转换为另一个数据类型。然而,不恰当的类型转换可能会导致未定义的行为,甚至引发进程异常退出。本文将深入分析一段C++代码,探讨其中由于类型转换不当导致的潜在问题,并解释为何这种类型转换可能引发进程异常退出......
  • 三种语言实现双指针解决数组元素的目标和(C++/Python/Java)
    题目给定两个升序排序的有序数组A和B,以及一个目标值x。数组下标从0开始。请你求出满足A[i]+B[j]=x的数对(i,j)。数据保证有唯一解。输入格式第一行包含三个整数n,m,x,分别表示A的长度,B的长度以及目标值x。第二行包含n个整数,表示数组A。第三行包含m个整数......
  • C++对象析构顺序问题——由QObject::desroyed展开的思考
    C++对象析构顺序问题——由QObject::desroyed展开的思考C++析构函数执行的顺序是最先执行继承链最末端的子类的,最后执行顶层的基类的。而QObject::destroyed(QObject*obj=nullptr)信号在Qt文档中说是“在obj被完全析构时之前立即触发,并且不会被阻塞”。这里的“完全析......
  • c++含有纯虚函数的虚基类是否能构造?
    前言笔者工作中有很多类派生自一个虚基类,但是这些派生类又有很多操作是大致相同,尤其是这些类都不能拷贝,所以需要删除拷贝构造函数和拷贝赋值运算符。最直接的想法是在虚基类里面删除拷贝构造函数和拷贝赋值运算符。但我想因为虚基类不能实例化,那是否可以定义常规构造函数、......
  • C/C++ 字面常量的注意事项
    在C/C++中使用字面常量时,有几个重要的注意事项需要考虑,以确保代码的准确性和可移植性。下面是一些关键要点:整数字面量:默认情况下,整数字面量是int类型。如果字面量的值超出了int的范围,它将被视为longint或longlongint,这取决于它的值和编译器。可以在整数字面量后添加L或l......
  • c++ 从txt读取数据 按照特殊字符拆分 gnss
      CMakeLists.txtcmake_minimum_required(VERSION3.10)project(ReadTextFile)#设置C++标准为C++11set(CMAKE_CXX_STANDARD11)set(CMAKE_CXX_STANDARD_REQUIREDTrue)#添加可执行文件,并链接主程序文件和自定义类的头文件add_executable(mainmain.cpp)......
  • 从C++看C#托管内存与非托管内存
    进程的内存一个exe文件,在没有运行时,其磁盘存储空间格式为函数代码段+全局变量段。加载为内存后,其进程内存模式增加为函数代码段+全局变量段+函数调用栈+堆区。我们重点讨论堆区。进程内存函数代码段全局变量段函数调用栈堆区托管堆与非托管堆C#inta=1......