C++11推出包装器,主要是为了解决函数式编程、泛型编程和回调机制中的几个核心痛点。这些包装器主要包括std::function、std::bind。
一、std::function
C++98/03都面临一个核心问题,就是无法统一表示"可调用的东西",例如以下的代码:
cpp
double calculateA(int a, int b, double rate)
{
return rate * (a + b);
}
double calculateB(int a, int b, double rate)
{
return rate * (a + b + 1);
}
struct calculateC
{
double operator()(int a, int b, double rate)
{
return rate * (a + b + 2);
}
};
auto calculateD = [](int x, int y, double z)->double { return (x + y + 3) * z; };
在 C++98 中,有多种"可以像函数一样调用"的实体:
- 普通函数:
double calculateA(int a, int b, double rate); - 函数指针:
void (*f)(int) = calculateA; - 函数对象(仿函数):
struct calculate { double operator()(int a, int b, double rate) {} }; - 成员函数指针:
&MyClass::method
但它们类型完全不同,无法用同一个类型变量存储!于是在C++11中就提出了包装器std::function
cpp
#include<functional>
double calculateA(int a, int b, double rate)
{
return rate * (a + b);
}
double calculateB(int a, int b, double rate)
{
return rate * (a + b + 1);
}
struct calculateC
{
double operator()(int a, int b, double rate)
{
return rate * (a + b + 2);
}
};
//包装器
int main()
{
int a = 1, b = 2;
double rate = 1.5;
auto calculateD = [](int x, int y, double z)->double { return (x + y + 3) * z; };
//以上代码中calculateA、calculateB、calculateC、calculateD基本上类型不同且要实现的功能是有差别的,
//但是这四个有一个大致相同的功能趋向,且返回值和参数一致。
//那么就可以对这些进行整合。从而就提出了包装器的概念。
std::function<double(int, int, double)> calA = calculateA;
std::function<double(int, int, double)> calB = calculateB;
std::function<double(int, int, double)> calC = calculateC();
std::function<double(int, int, double)> calD = calculateD;
std::cout << calA(a, b, rate) << std::endl;
std::cout << calB(a, b, rate) << std::endl;
std::cout << calC(a, b, rate) << std::endl;
std::cout << calD(a, b, rate) << std::endl;
return 0;
}
如上代码使用std::function<Ret(Args...)>来完成对这些调用对象类型的统一。这时就因为类型的统一,就可以对这统一的对象进行管理、存储及使用,leetcode中150. 逆波兰表达式求值 - 力扣(LeetCode)

cpp
#include<vector>
#include<functional>
#include<map>
class Solution {
public:
int evalRPN(vector<string>& tokens) {
map<string,function<int(int,int)>> RPN={
{"+",[](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;}}
};
stack<string> st;
for(auto& x:tokens)
{
if(RPN.count(x))//是符号
{
int right=stoi(st.top());
st.pop();
int left = stoi(st.top());
st.pop();
st.push(to_string(RPN.find(x)->second(left,right)));
}
else
{
st.push(x);
}
}
return stoi(st.top());
}
};
以上代码中,将所有的计算都存储在RPN这个对象中,就很好的整合了这些代码,并且对比不用包装器的代码,很清楚的就可以发现使用包装器可以减少一些工作量,并且如果后续更新运算也只需要对RPN做出更改,而不需要更改其他任何地方,使得代码的耦合降低。
二、std::bind
std::bind函数定义在头文件中,是一个函数模板,他就像一个函数包装器,接受一个调用对象,生成一个新的调用对象来"适应"原对象的参数列表。使用std::bind就可以把一个原本接收N个参数的函数,通过绑定一些参数返回M(<N)个参数的新函数。同时可以实现参数顺序调整的操作。
cpp
simple(1)
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的模板
cpp
#include<functional>
int sub(int a, int b)
{
return a - b;
}
double calculateA(int a, int b, double rate)
{
return rate * (a - b);
}
class calculateC
{
public:
double operator()(int a, int b, double rate)
{
return rate * ((double)a - (double)b + 2);
}
double calculateB(int a, int b, double rate)
{
return rate * (a - b);
}
};
//包装器
int main()
{
int a = 1, b = 2;
double rate = 1.5;
//改变接收参数顺序
std::function<int(int, int)> rsub1 = bind(sub, placeholders::_1, placeholders::_2);
std::function<int(int, int)> rsub2 = bind(sub, placeholders::_2, placeholders::_1);
cout << rsub1(a, b) << endl;//-1
cout << rsub2(a, b) << endl;// 1
//绑定参数rate
std::function<double(int, int)> ccalA1 = bind(calculateA, placeholders::_2, placeholders::_1,0.5);
std::function<double(int, int)> ccalA2 = bind(calculateA, placeholders::_2, placeholders::_1, 1.5);
calculateC calC;
std::function<double(int, int)> ccalA3 = bind(&calculateC::calculateB, calC, placeholders::_2, placeholders::_1, 2.5);
cout << ccalA1(a, b) << endl;//0.5
cout << ccalA2(a, b) << endl;//1.5
cout << ccalA3(a, b) << endl;//2.5
return 0;
}
在代码中
std::function<double(int, int)> ccalA3 = bind(&calculateC::calculateB, calC, placeholders::_2, placeholders::_1, 2.5);
✅ 必须传入对象 calC(或其引用/指针) ,而 ❌ 不能写成 calculateC::calculateB()。
🔍 一、根本原因:成员函数 ≠ 普通函数
在 C++ 中:
- 普通函数(如
sub,calculateA) 是独立的,可以直接调用。 - 成员函数(如
calculateC::calculateB) 必须通过某个对象实例 才能调用,因为它隐式依赖this指针。
也就是说:
calC.calculateB(a, b, rate); // ✅ 正确:通过对象 calC 调用
calculateC::calculateB(a, b, rate); // ❌ 错误!缺少 this 指针
💡 成员函数本质上是:
double calculateB(calculateC* this, int a, int b, double rate)
所以,当你取成员函数地址时:
&calculateC::calculateB // 类型是:double (calculateC::*)(int, int, double)
它只是一个"函数指针",没有绑定到任何对象,不能直接调用!
✅ 二、std::bind 如何调用成员函数?
std::bind 的第一个参数是 成员函数指针 ,第二个参数必须是该类的对象(或引用/指针) ,用来提供 this。
bind(&Class::member_func, object_or_ptr, arg1, arg2, ...)
你的例子分解:
bind(
&calculateC::calculateB, // 成员函数指针
calC, // 提供 this → bind 内部会调用 calC.calculateB(...)
placeholders::_2, // 对应第一个 int 参数(实际是 b)
placeholders::_1, // 对应第二个 int 参数(实际是 a)
2.5 // 固定 rate = 2.5
)
最终效果相当于:
// 当你调用 ccalA3(a, b) 时,内部执行:
calC.calculateB(b, a, 2.5); // 注意参数顺序被交换了!
❌ 三、为什么不能写 calculateC::calculateB()?
尝试 1:直接写函数名(不取地址)
bind(calculateC::calculateB, ...) // ❌ 编译错误!
calculateC::calculateB不是可调用对象(除非是 static 成员函数)- 它不是一个值,不能作为表达式传递
尝试 2:试图"调用"它
bind(calculateC::calculateB(), ...) // ❌ 更错!
calculateC::calculateB()是调用一个不存在的对象的成员函数 → 非法- 即使能编译,也会立即执行一次(返回一个
double),而不是生成一个可调用对象
🚫 成员函数必须和对象绑定才能形成可调用实体!