std::bind
What problem does this section solve?
Sometimes you already have a function, but its parameters don't match the target interface.
- I want to predefine certain parameters.
- I'd like to adjust the order of the parameters. Which system or tool are you working with—such as a microcontroller, Robot Operating System, or something else? I can help you rearrange the parameters once I know the context.
- 想把成员函数绑定到某个对象上,变成普通回调。
std::bind can adapt existing callable objects into new callable objects.
In modern C++, many bind scenarios can be written more clearly with lambdas. However, std::bind still frequently appears in legacy projects, ROS2 examples, Boost.Asio callbacks, and member function adapters—it remains worth mastering.
What is this feature?
std::bind takes a callable object and a set of bound parameters, returning a new callable object. When invoking the new object, the placeholders _1, _2, and _3 represent the first, second, and third arguments passed at call time.
std::bind requires the header file <functional>, with the placeholder in the namespace std::placeholders.
| writing method | Meaning |
|---|---|
std::bind(f, 10, _1) | Set the first parameter of f to 10. |
std::bind(f, _2, _1) | Swap the order of the two parameters when calling. |
std::bind(&C::m, &obj, _1) | Bind member functions to objects obj |
std::bind(f, std::ref(x), _1) | Bind references, not copies. |
Example code
Example 1: Fixed partial arguments of a regular function
#include <functional>
#include <iostream>
#include <string>
void print_student(const std::string& name, int age, int score)
{
std::cout << name << " age=" << age << " score=" << score << "\n";
}
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
using std::placeholders::_1;
using std::placeholders::_2;
print_student("Alice", 20, 95);
// bind 会把函数和部分参数提前绑定成一个可调用对象。
auto print_bob = std::bind(print_student, "Bob", _1, _2);
print_bob(21, 88);
auto print_charlie_score = std::bind(print_student, "Charlie", 22, _1);
print_charlie_score(91);
return 0;
}
Results:
Alice age=20 score=95
Bob age=21 score=88
Charlie age=22 score=91
Example 2: Adjust Parameter Order
The position of placeholders determines the order of parameters received by the original function.
#include <functional>
#include <iostream>
void divide(int a, int b)
{
std::cout << a << " / " << b << " = " << (a / b) << "\n";
}
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
using std::placeholders::_1;
using std::placeholders::_2;
divide(10, 2);
// bind 会把函数和部分参数提前绑定成一个可调用对象。
auto swapped = std::bind(divide, _2, _1);
swapped(2, 10);
auto half = std::bind(divide, _1, 2);
half(10);
half(20);
return 0;
}
Results:
10 / 2 = 5
10 / 2 = 5
10 / 2 = 5
20 / 2 = 10
Example 3: Binding Member Functions
Non-static member functions require an object to be called. The second parameter of std::bind is typically an object pointer, object reference, or smart pointer.
#include <functional>
#include <iostream>
#include <string>
class Printer
{
std::string prefix_;
public:
explicit Printer(const std::string& prefix) : prefix_(prefix) {}
void print(int id, const std::string& text) const
{
std::cout << prefix_ << " #" << id << ": " << text << "\n";
}
};
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
using std::placeholders::_1;
using std::placeholders::_2;
Printer printer("LOG");
// bind 会把函数和部分参数提前绑定成一个可调用对象。
auto print_any = std::bind(&Printer::print, &printer, _1, _2);
print_any(7, "ready");
auto print_ok = std::bind(&Printer::print, &printer, _1, "ok");
print_ok(8);
return 0;
}
Results:
LOG #7: ready
LOG #8: ok
This &printer must remain valid throughout the use of print_any and print_ok. If the bound callback is to be saved for a longer duration, it is generally necessary to consider smart pointers or more explicit lifecycle management.
Example 4: Comparison between bind and Lambda
For the same parameter adaptation task, Lambda tends to be more intuitive, since both parameter names and call relationships are clearly written out.
#include <functional>
#include <iostream>
int weighted_sum(int base, int x, int y)
{
return base + x * 10 + y;
}
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
using std::placeholders::_1;
using std::placeholders::_2;
// bind 会把函数和部分参数提前绑定成一个可调用对象。
auto by_bind = std::bind(weighted_sum, 100, _1, _2);
auto by_lambda = [](int x, int y) {
return weighted_sum(100, x, y);
};
std::cout << "bind result = " << by_bind(3, 4) << "\n";
std::cout << "lambda result = " << by_lambda(3, 4) << "\n";
auto swapped_bind = std::bind(weighted_sum, 100, _2, _1);
auto swapped_lambda = [](int x, int y) {
return weighted_sum(100, y, x);
};
std::cout << "swapped bind result = " << swapped_bind(3, 4) << "\n";
std::cout << "swapped lambda result = " << swapped_lambda(3, 4) << "\n";
return 0;
}
Results:
bind result = 134
lambda result = 134
swapped bind result = 143
swapped lambda result = 143
Example 5: By default, bind copies its parameters, so use std::ref when a reference is needed.
std::bind copies parameters by default when saving. If you want to bind references, you must explicitly use std::ref or std::cref.
#include <functional>
#include <iostream>
void add_to(int& total, int value)
{
total += value;
std::cout << "inside total = " << total << "\n";
}
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
int total = 0;
// bind 会把函数和部分参数提前绑定成一个可调用对象。
auto add_copy = std::bind(add_to, total, 5);
add_copy();
std::cout << "outside after copy bind = " << total << "\n";
auto add_ref = std::bind(add_to, std::ref(total), 5);
add_ref();
std::cout << "outside after ref bind = " << total << "\n";
return 0;
}
Results:
inside total = 5
outside after copy bind = 0
inside total = 5
outside after ref bind = 5
Key Grammar Explanation
| writing method | Explanation | Precautions |
|---|---|---|
_1 | The first parameter passed when calling a new function | From std::placeholders |
_2 | The second parameter passed when calling a new function. | can be used to adjust the order |
| constant | Write directly into the bind parameter. | It will be copied and saved by default. |
&Class::method | pointer to member function | Still need to bind objects later. |
std::ref(x) | Bind by reference x | Not writing std::ref usually involves copying. |
How to choose between bind and Lambda
| Scene | Recommendation |
|---|---|
| Simple fixed parameters | Lambda or bind are both fine. |
| The parameter relationships need to be understood clearly. | Lambda |
Binding member functions to legacy interfaces can be tricky due to how member functions implicitly capture the this pointer. Below are some common approaches to handle this scenario: |
1. Use a Static Member Function with a Global/Object Map
class MyClass {
public:
void callback(int data) { /* ... */ }
static void staticCallback(int data) {
// Retrieve the object instance (e.g., via a map or static member)
MyClass* instance = getInstance();
instance->callback(data);
}
};
// Example mapping mechanism (simplified)
MyClass* MyClass::getInstance() {
static MyClass instance;
return &instance;
}
// Register the static function with the legacy interface
legacyRegister(MyClass::staticCallback);
2. Use a Free Function with a Capturing Lambda (C++11+)
MyClass* obj = new MyClass();
// Wrap the member function in a lambda
legacyRegister([obj](int data) {
obj->callback(data);
});
// Remember to manage object lifetime to avoid dangling pointers.
3. Use std::bind (C++11)
MyClass obj;
auto boundFunc = std::bind(&MyClass::callback, &obj, std::placeholders::_1);
legacyRegister(boundFunc);
Key Considerations
- Lifetime Management: Ensure the bound object outlives the callback usage.
- Thread Safety: If callbacks occur across threads, synchronize access accordingly.
- Function Signature: Match the expected signature of the legacy interface (argument types, return type).
Choose the method that best fits your project's constraints and codebase style. For simple cases, a lambda with explicit capture is often the most readable solution.|bind is very common, and you can also use Lambda.|
|Requires complex logic and conditional judgment.|Lambda|
|Reading old code or ROS2/Boost.Asio examples|Must understand bind|
In a word: new code prioritizes Lambda; when adapting existing interfaces, legacy project code, or member function callbacks, need to be adept at interpreting std::bind.
Common Errors
- Forgot where the placeholder comes from
std::placeholders. - mistook the order of
_1and_2, resulting in incorrect actual parameter sequence. - Forgot to bind the object when binding member function.
- After binding the object pointer, the object is destroyed first while the callback is still in use.
- By default,
bindsaves parameters by reference. The default is to copy; when a reference is needed, usestd::ref.
使用建议
- 明确目标:在开始前确定您的具体需求,以便选择最合适的工具或教程。
- 充分利用资源:参考官方文档、教程和博客,这些资料能帮助您快速上手并解决问题。
- 实践应用:通过动手操作项目或编写代码来巩固学习成果,提升实际操作能力。
- 问题解决:遇到困难时,查阅参考资料或寻求社区支持,逐步培养独立解决问题的能力。
- 分享经验:完成项目后,可以撰写文章或博客分享心得,帮助其他学习者。
如果需要针对特定领域(如单片机、机器人或环境搭建)的进一步建议,请提供更多信息,我将为您细化内容。
- In the new code, parameter adaptation prioritizes attempting Lambda.
- When reading ROS2, Boost.Asio, and legacy project code, it's essential to understand
std::bind(&Class::method, this, _1, _2). - When binding member functions, first and foremost consider the object lifetime.
- When you need to refer to semantics, explicitly write
std::reforstd::cref. - Avoid creating hard-to-read placeholder combinations just to use
bind. Clarity is more important than showing off.
Summary
std::bindis used to fix parameters, adjust their order, and bind member functions._1and_2indicate the parameter positions passed when calling the new callable.bindcopies bound parameters by default; usestd::refwhen referencing is needed.- In modern C++, many
binduse cases can be replaced with Lambdas. - Understanding
bindis quite helpful for reading ROS2, Boost.Asio, and legacy C++ projects.