C++11 —— 包装器
- 包装器
- function包装器
- function包装器介绍
- function包装器统一类型
- function包装器的意义
- bind包装器
- bind包装器介绍
- bind包装器绑定固定参数
- bind包装器调整传参顺序
- bind包装器的意义
包装器
function包装器
function包装器介绍
function
包装器 也叫作适配器。C++ 中的 function
本质是一个类模板,也是一个包装器。这些包装器主要用于提供一致且适合的接口,以便于处理各种可调用对象,如函数、函数对象、lambda表达式等。
std::function
的原型如下:
template< class R, class... Args >
class function<R(Args...)>;
其中:
- R: 是被调用函数的返回类型。
- Args: 是被调用函数的参数类型列表。
std::function<int(int, int)> func; // func 可以存储接受两个 int 参数并返回 int 的可调用对象
function包装器统一类型
std::function
是一个类模板,它提供了一种统一的方式来存储和调用各种可调用对象,包括:
- 普通函数
- Lambda 表达式
- 函数指针
- 函数对象(重载了 operator() 的类)
- 成员函数指针
- 数据成员指针
通过指定模板参数,std::function
可以处理具有特定返回类型和参数类型列表的可调用对象。它使用模板转换构造函数来接收被包装的函数对象。
下面这段代码,useF
是一个函数模板,接受两个模板参数F
和T
。
F
表示可调用对象的类型,T
表示参数的类型
使用可调用对象f
,传入参数x
,同时声明静态整形变量count
以记录函数被调用的次数。
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;
}
可以看到这里的count
都是1,并且这里的地址也都不相同,说明这里的useF
函数模板被实例化了三份。
但其实这里没必要实例化三份出来,虽然传入的可调用对象的类型不相同,但是外层显示看到的参数和返回值都是相同的,此时就可以使用包装器来进行上层封装,来优化效率,只实例化一个模板。
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;
cout << endl << endl;
function<double(double)> func1 = f;
cout << useF(func1, 11.11) << endl;
function<double(double)> func2 = Functor();
cout << useF(func2, 11.11) << endl;
function<double(double)> func3 = [](double d)->double {return d / 4; };
cout << useF(func3, 11.11) << endl;
return 0;
}
进行包装后可见,打印出来的count的每次地址都是相同的,并且count被累加到3,说明function做到了类型的统一,并且让其调用了3次。
使用 std::function
的一些注意事项:
- 转换后的
std::function
对象的参数类型必须能转换为可调用对象的参数类型。 - 可调用对象的返回类型必须能转换为
std::function
对象的返回类型。 - 如果尝试调用一个存储了空可调用对象的
std::function
,会抛出std::bad_function_call
异常。
function包装器的意义
C++中存在多种可调用对象,包括普通函数、仿函数和lambda表达式。使用包装器(如std::function)可以将这些不同的可调用对象统一为一种类型,从而简化调用和管理。
比如下面的代码,在调用的时候就会显得比较凌乱,不统一,不符合C++11的编程习惯:
//包装器
#include <functional>
int f(int a, int b)
{
return a + b;
}
struct F
{
int operator()(int a,int b)
{
return a + b;
}
};
int main()
{
cout << f(1, 2) << endl;
cout << "------------------" << endl;
F f1;
cout << f1(2, 3) << endl;
cout << "------------------" << endl;
cout << F()(3, 4) << endl;
cout << "------------------" << endl;
cout << [](int a, int b) {return a + b; }(4, 5) << endl;
cout << "------------------" << endl;
return 0;
}
所以使用包装器,可以做到接口的统一化,同时function
也同时提供了类型安全的接口。通过定义返回类型和参数类型,编译器能够在编译的时候检查类型一致性,避免运行时错误。
包装器也减少了模板参数化的数量,从而提高了编译效率,通过将相同特征标的可用调用对象统一为一个类型,避免了多次实例化的问题!
// 使用 std::function 包装普通函数 f
function<int(int, int)> func1 = f; // func1 是一个可调用对象,类型为 int(int, int)
cout << func1(1, 2) << endl; // 调用 func1,输出 3(1 + 2)
cout << "------------------" << endl;
// 使用 std::function 包装临时的 F 对象
function<int(int, int)> func2 = F(); // 创建 F 的临时对象并包装
cout << func2(2, 3) << endl; // 调用 func2,输出 5(2 + 3)
cout << "------------------" << endl;
// 创建 F 的实例 f1,并使用 std::function 包装它
F f1; // 实例化 F
function<int(int, int)> func3 = f1; // 将 f1 包装到 func3 中
cout << func3(3, 4) << endl; // 调用 func3,输出 7(3 + 4)
cout << "------------------" << endl;
// 使用 std::function 包装 lambda 表达式
function<int(int, int)> func4 = [](int a, int b) { return a + b; }; // lambda 表达式
cout << func4(4, 5) << endl; // 调用 func4,输出 9(4 + 5)
cout << "------------------" << endl;
bind包装器
bind包装器介绍
bind
也是一种函数包装器,也叫做适配器。它可以接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表,C++中的bind
本质是一个函数模板。
bind函数模板的原型如下:
template<typename F, typename... Args>
auto bind(F&& f, Args&&... args);
模板参数说明:
F
是可调用对象的类型。Args
是绑定的参数类型,可以是值或占位符。
bind包装器绑定固定参数
可以使用 std::bind
将某些参数固定,从而生成一个新的可调用对象。例如:
#include <iostream>
#include <functional>
void printSum(int a, int b, int c) {
std::cout << "Sum: " << (a + b + c) << std::endl;
}
int main() {
// 绑定固定参数
auto boundFunc = std::bind(printSum, 1, 2, std::placeholders::_1);
// 调用时只需要提供最后一个参数
boundFunc(3); // 输出: Sum: 6
return 0;
}
在这个示例中,printSum
函数的前两个参数被固定为 1 和 2,而第三个参数使用占位符 _1,在调用boundFunc
时提供。
bind包装器调整传参顺序
std::bind 还可以通过占位符调整参数的顺序。例如:
#include <iostream>
#include <functional>
void printValues(int a, int b, int c) {
std::cout << "Values: " << a << ", " << b << ", " << c << std::endl;
}
int main() {
// 调整参数顺序
auto boundFunc = std::bind(printValues, std::placeholders::_2, 42, std::placeholders::_1);
// 调用时提供两个参数
boundFunc(10, 20); // 输出: Values: 20, 42, 10
return 0;
}
在上面的代码中,printValues
函数的参数顺序被调整,第二个参数固定为 42,而 _1 和 _2 分别对应调用时提供的第一个和第二个参数。
bind包装器的意义
std::bind
是 C++11 中的一个函数适配器,它允许将可调用对象与部分参数绑定,从而生成新的可调用对象,简化函数调用并提高代码可读性。通过绑定固定参数和调整参数顺序,std::bind
提供了灵活性,使得在回调函数和事件处理等场景中,开发者能够更方便地管理和使用各种可调用对象。