目录
😊前言:在现代 C++ 中,
std::function
是一个非常有用的工具,它使得函数能够像对象一样传递、存储和调用。随着 C++11 的到来,std::function
被引入到标准库中,成为函数式编程和回调机制的核心组件之一。在这篇博客中,我们将深入探讨std::function
的工作原理、应用场景及其优缺点。
1.什么是 std::function
?
std::function
是 C++11 引入的一个模板类,用于封装任何可调用对象(如普通函数、Lambda 表达式、函数指针、成员函数指针或函数对象等)。它允许你存储一个可调用对象,并在需要时调用它。这使得我们可以更加灵活地编写代码,特别是在需要传递回调函数或异步任务时,std::function
显得尤为重要。
std::function
是通过类型擦除实现的,它可以在运行时动态地将不同类型的可调用对象转化为统一的接口。简单来说,它允许你用一个通用的对象来代替不同类型的函数或函数指针。
2. function
包装器的原型
std::function在头文件<functional>
// 类模板原型如下
template <class T> function; // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
模板参数说明:
Ret: 被调用函数的返回类型Args…:被调用函数的形参。
3.使用 function
封装不同类型的函数对象
#include <iostream>
#include <functional>
#include <string>
using namespace std;
// 普通函数
void func(int n)
{
cout << "普通函数: " << n << endl;
}
// 仿函数
struct Func
{
void operator()(int n)
{
cout << "仿函数: " << n << endl;
}
};
// Lambda 表达式
auto lambda = [](int n) { cout << "Lambda 表达式: " << n << endl; };
int main()
{
// 使用 std::function 封装不同类型的函数
function<void(int)> f;
f = func; // 包装普通函数
f(10);
f = Func(); // 包装仿函数
f(20);
f = lambda; // 包装 Lambda 表达式
f(30);
return 0;
}
代码分析
我们定义了三个不同类型的函数:一个普通函数 func
、一个仿函数 Func
和一个 Lambda 表达式 lambda
。
然后,使用 std::function<void(int)>
来封装这三种不同类型的函数对象。
通过调用包装后的 f,我们可以统一的方式执行这些不同的函数对象。(适配器)
这种方式使得我们能够将多种类型的可调用对象统一为一个接口,方便管理和使用。
4.实际应用:
解法一:
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> st;
for(auto& to:tokens)
{
if(to=="+"||to=="-"||to=="*"||to=="/")
{
int right=st.top();
st.pop();
int left=st.top();
st.pop();
switch(to[0])
{
case '+':
st.push(left+right);
break;
case '-':
st.push(left-right);
break;
case '*':
st.push(left*right);
break;
case '/':
st.push(left/right);
break;
}
}
else
{
st.push(stoi(to));
}
}
return st.top();
}
};
解法二: 利用function包装器:
class Solution
{
public:
int evalRPN(vector<string>& tokens)
{
// 解题思路:操作数入栈,遇到操作符,取两个数计算后,入栈
// 建立映射关系
unordered_map<string, function<int(int, int)>> hash =
{
{"+", [](int x, int y)->int { return x + y; } },
{"-", [](int x, int y)->int { return x - y; } },
{"*", [](int x, int y)->int { return x * y; } },
{"/", [](int x, int y)->int { return x / y; } },
};
stack<int> s;
for(auto str : tokens)
{
if(str != "+" && str != "-" && str != "*" && str != "/")
s.push(stoi(str));
else
{
// 注意:先获取 y,再获取 x
int y = s.top();
s.pop();
int x = s.top();
s.pop();
s.push(hash[str](x, y));
}
}
return s.top();
}
};
function作为C++11的一个知识,还是非常好用的。😊
5. bind
绑定:修改参数传递顺序和数量
bind
是 C++ 标准库中的一个函数模板,它允许我们对函数参数进行预先绑定或重新排列,从而生成一个新的可调用对象。bind
的强大之处在于,它不仅能够指定某些参数的固定值,还能改变参数传递的顺序,极大地提高了灵活性。
函数原型:
template <class Fn, class... Args>
bind (Fn&& fn, Args&&... args);
fn
是传递的 函数对象,args
是传给函数的 可变参数包,这里使用了 万能引用(引用折叠),使其在进行模板类型推导时,既能引用左值,也能引用右值。
2.1 使用 bind
绑定修改参数传递顺序
#include <iostream>
#include <functional>
using namespace std;
void Func(int a, int b)
{
cout << "Func: " << a << " " << b << endl;
}
int main()
{
// 正常调用
Func(10, 20);
// 使用 bind 改变参数顺序
auto RFunc = bind(Func, std::placeholders::_2, std::placeholders::_1);
RFunc(10, 20); // 输出: Func: 20 10
return 0;
}
代码分析
bind(Func, std::placeholders::_2, std::placeholders::_1)
通过 placeholders::_1
和 placeholders::_2
指定了新的参数顺序,即将原本的第二个参数和第一个参数交换。
当我们调用 RFunc(10, 20)
时,实际上是将 20
作为第一个参数,10
作为第二个参数传递给 Func
。
这种参数顺序的改变,在一些特定的应用场景下非常有用,特别是在函数签名不一致时,可以方便地进行适配。
2.2. bind
绑定:指定特定参数
bind
还可以用于指定函数的某些参数为固定值,从而减少后续调用时需要传递的参数个数。
示例代码:使用 bind
绑定指定特定参数
#include <iostream>
#include <functional>
using namespace std;
void Func(int a, int b)
{
cout << "Func: " << a << " " << b << endl;
}
int main()
{
// 使用 bind 绑定第一个参数
auto RFunc = bind(Func, 100, std::placeholders::_1);
RFunc(20); // 输出: Func: 100 20
return 0;
}
代码分析
我们通过 bind(Func, 100, std::placeholders::_1)
将第一个参数绑定为固定值 100
。
后续调用时,我们只需要传递第二个参数 20
,bind
会自动将 100
作为第一个参数传递给 Func
。
2.3. bind
绑定与类成员函数
bind
还可以用于绑定类成员函数。对于普通函数,绑定非常简单,但对于成员函数,我们需要额外注意如何传递类的对象或指针。
示例代码:使用 bind
绑定静态成员函数
#include <iostream>
#include <functional>
using namespace std;
class Test
{
public:
static void funcA(int val)
{
cout << "静态成员函数 funcA: " << val << endl;
}
};
int main()
{
// 使用 bind 绑定静态成员函数
auto RFunc = bind(&Test::funcA, std::placeholders::_1);
RFunc(10); // 输出: 静态成员函数 funcA: 10
return 0;
}
代码分析
对于静态成员函数,我们可以直接使用 &Test::funcA
来绑定。
bind
会自动处理函数的绑定,并返回一个新的可调用对象 RFunc
,我们可以使用它来调用函数。
示例代码:使用 bind
绑定非静态成员函数
#include <iostream>
#include <functional>
using namespace std;
class Test
{
public:
Test(int n) : _n(n) {}
void funcB(int val)
{
cout << "非静态成员函数 funcB: " << val * _n << endl;
}
private:
int _n;
};
int main()
{
Test t(10);
// 使用 bind 绑定非静态成员函数
auto RFunc = bind(&Test::funcB, t, std::placeholders::_1);
RFunc(5); // 输出: 非静态成员函数 funcB: 50
return 0;
}
代码分析
对于非静态成员函数,我们需要提供类的对象 t
作为参数来绑定。
bind
会将 t
与 &Test::funcB
结合,并生成一个新的可调用对象。
总结:😊
通过
std::function
和bind
,C++ 提供了强大的函数包装和绑定功能,使得我们能够在不同类型的函数之间进行无缝切换、修改参数传递顺序以及绑定特定参数。这些工具极大地增强了代码的灵活性和可重用性,特别是在需要对多个不同函数进行统一管理时,它们提供了非常便捷的解决方案。在实际开发中,这些技巧不仅能帮助我们提升编程效率,还能让代码更加简洁和优雅。