C++11——— 包装器

1.function

1.1 function包装器介绍

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

function类模板的原型如下:

cpp 复制代码
template <class T>
class function; // undefined
//只要你传入的 T 不是我后面特化的类型,就直接编译报错!
template <class Ret, class... Args>
class function<Ret(Args...)>;

模板参数说明:

Ret:被包装的可调用对象的返回值类型。

Args...:被包装的可调用对象的形参类型。

cpp 复制代码
#include<functional>
//1.函数
int f(int a, int b)
{
 return a + b;
}
//2.仿函数
struct Functor
{
public:
 int operator() (int a, int b)
 {
 return a + b;
 }
};
//4.类成员函数
class Plus
{
public:
 Plus(int n = 10)
 :_n(n)
 {}
 static int plusi(int a, int b)
 {
 return a + b;
 }
 double plusd(double a, double b)
 {
 return (a + b) * _n;
 }
private:
int _n;
};

int main()
{
 // 包装各种可调⽤对象 
 function<int(int, int)> f1 = f;//1
 function<int(int, int)> f2 = Functor();//2
 function<int(int, int)> f3 = [](int a, int b) {return a + b; };//3
 cout << f1(1, 1) << endl;
 cout << f2(1, 1) << endl;
 cout << f3(1, 1) << endl;
 
 // 4.包装静态成员函数 
 // 成员函数要指定类域并且前⾯加&才能获取地址 
 function<int(int, int)> f4 = &Plus::plusi;
 cout << f4(1, 1) << endl;
 
 // 包装普通成员函数 
 // 普通成员函数还有⼀个隐含的this指针参数,所以绑定时传对象或者对象的指针过去都可以 
 //1.传对象指针
 function<double(Plus*, double, double)> f5 = &Plus::plusd;
 Plus pd;
 cout << f5(&pd, 1.1, 1.1) << endl;
 //2.传对象
 function<double(Plus, double, double)> f6 = &Plus::plusd;
 cout << f6(pd, 1.1, 1.1) << endl;
//3.传右值 / 临时对象
 function<double(Plus&&, double, double)> f7 = &Plus::plusd;
 cout << f7(move(pd), 1.1, 1.1) << endl;
 cout << f7(Plus(), 1.1, 1.1) << endl;
 return 0;
}
function 模板参数 第一个参数传什么
double(Plus*, ...) 对象指针 &pd
double(Plus, ...) 对象 pd
double(Plus&&, ...) 临时对象 /move 对象

std::function 是⼀个类模板,也是⼀个包装器。 std::function 的实例对象可以包装存

储其他的可以调⽤对象,包括函数指针、仿函数、 lambda 、 bind 表达式等,存储的可调⽤对象被称为std::function 的⽬标。若std::function 不含⽬标,则称它为空。调⽤空std::function 的⽬标导致抛出std::bad_function_call异常。

1.2function包装器统一类型

函数指针、仿函数、 lambda 等可调⽤对象的类型各不相同, std::function 的优势就是统⼀类型,对他们都可以进⾏包装,这样在很多地⽅就⽅便声明可调⽤对象的类型

cpp 复制代码
#include <iostream>
#include <functional>
#include <vector>

using namespace std;

// 1. 普通函数
int add(int a, int b) {
    return a + b;
}

// 2. 仿函数(函数对象)
struct Multiply {
    int operator()(int a, int b) {
        return a * b;
    }
};

// 3. 类 + 成员函数
class MyClass {
public:
    int sub(int a, int b) {
        return a - b;
    }
};

int main() {
   
    // 重点:用 function 把四种完全不同的可调用对象
    // 全部包装成同一种类型:function<int(int, int)>
    

    // 1. 包装普通函数
    function<int(int, int)> f1 = add;

    // 2. 包装 lambda
    function<int(int, int)> f2 = [](int a, int b) { return a - b; };

    // 3. 包装仿函数
    function<int(int, int)> f3 = Multiply();

    // 4. 包装类成员函数(第一个参数是对象/指针)
    MyClass obj;
    function<int(MyClass*, int, int)> f4 = &MyClass::sub;


   
    // 放进同一个 vector(统一类型)
   
    vector<function<int(int, int)>> vec;

    vec.push_back(f1);
    vec.push_back(f2);
    vec.push_back(f3);

    // 调用
    cout << vec[0](10, 20) << endl; // add  30
    cout << vec[1](10, 20) << endl; // lambda -10
    cout << vec[2](10, 20) << endl; // Multiply 200

    return 0;
}

std::function 就是一个 "类型适配器",把所有可调用对象,包装成统一的类型

1.3function包装器的意义

将可调用对象的类型进行统一,便于我们对其进行统一化管理。

包装后明确了可调用对象的返回值和形参类型,更加方便使用者使用。

2.bind包装器

bind也是一种函数包装器,也叫做适配器。它可以接受一个可调用对象,生成一个新的可调用对象来"适应"原对象的参数列表,C++中的bind本质是一个函数模板。

1.可以固定部分参数

2.可以调整参数顺序

3.可以把成员函数转成普通函数

4.最终得到一个可直接调用的新函数对象

bind函数模板的原型如下:

cpp 复制代码
template <class Fn, class... Args>
/* unspecified */ bind(Fn&& fn, Args&&... args);

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

Fn:要绑定的可调用对象类型(函数、lambda、成员函数、仿函数)

Args&&...:可变参数模板,支持任意个数、任意类型的参数

Ret:手动指定返回值类型

调⽤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为第⼆个参数,以此类推。_1/_2/_3...这些占位符放到placeholders的⼀个命名空间中。

2.1用法
bind包装器绑定固定参数

1.无意义的绑定

cpp 复制代码
int Plus(int a, int b)
{
	return a + b;
}
int main()
{
	//无意义的绑定
	function<int(int, int)> func = bind(Plus, placeholders::_1, placeholders::_2);
	cout << func(1, 2) << endl; //3
	return 0;
}

绑定时第一个参数传入函数指针这个可调用对象,但后续传入的要绑定的参数列表依次是placeholders::_1和placeholders::_2,表示后续调用新生成的可调用对象时,传入的第一个参数传给placeholders::_1,传入的第二个参数传给placeholders::_2。此时绑定后生成的新的可调用对象的传参方式,和原来没有绑定的可调用对象是一样的,所以说这是一个无意义的绑定。

2.绑定固定参数

如果想把Plus函数的第二个参数固定绑定为10,可以在绑定时将参数列表的placeholders::_2设置为10。比如:

cpp 复制代码
int Plus(int a, int b)
{
	return a + b;
}
int main()
{
	//绑定固定参数
	function<int(int)> func = bind(Plus, placeholders::_1, 10);
	cout << func(2) << endl; //12
	return 0;
}

2.bind包装器调整传参顺序

想要将sub成员函数用于相减的两个参数的顺序交换,那么直接在绑定时将placeholders::_1和placeholders::_2的位置交换一下就行了。比如:

cpp 复制代码
class Sub
{
public:
	int sub(int a, int b)
	{
		return a - b;
	}
};
int main()
{
	//调整传参顺序
	function<int(int, int)> func = bind(&Sub::sub, Sub(), placeholders::_2, placeholders::_1);
	cout << func(1, 2) << endl; //1
	return 0;
}

3.绑定成员函数

bind 绑成员函数时,第二个位置必须传对象 / 指针 / 引用,它就是用来代替隐藏的 this 指针!

cpp 复制代码
class MyClass {
public:
    int sub(int a, int b) {
        return a - b;
    }
};
MyClass obj;
auto f3 = bind(&MyClass::sub, obj, _1, _2);
    cout << f3(10, 3) << endl; // 10-3=7

相当于

cpp 复制代码
f3(10, 3); 
// 内部实际调用:
obj.sub(10, 3);

注意

std::bind 能够不拷贝,依靠四个核心机制:

可变参数模板:接收任意参数

通用引用(T&&)+ 引用折叠:保留值类别

完美转发:保持左值 / 右值属性,不强制拷贝

std::reference_wrapper:显式传递引用,实现真正零拷贝

std::reference_wrapper 的作用:

绑定类成员函数时,用引用包装器存储对象地址,避免拷贝对象;

调用时自动解包为原对象引用,最终正确调用该对象的成员函数。

相关推荐
zihao_tom2 小时前
Go环境搭建(vscode调试)
开发语言·vscode·golang
IT方大同2 小时前
(实时操作系统)线程管理
c语言·开发语言·嵌入式硬件
十年编程老舅3 小时前
Linux 多线程高并发编程:读写锁的核心原理与底层实现
linux·c++·linux内核·高并发·线程池·多线程·多进程
阿kun要赚马内3 小时前
Python面向对象:@property装饰器
开发语言·前端·python
sunwenjian8863 小时前
Java进阶--IO流
java·开发语言
wildlily84273 小时前
C++ Primer 第5版章节题 第十三章(二)
开发语言·c++
意法半导体STM323 小时前
【官方原创】STM32H7双核芯片通过 STlink连接失败问题分析 LAT1654
开发语言·前端·javascript·stm32·单片机·嵌入式硬件
深蓝海拓3 小时前
使用@property将类方法包装为属性
开发语言·python
xiaoye-duck3 小时前
【C++:unordered_set和unordered_map】 深度解析:使用、差异、性能与场景选择
开发语言·c++·stl