7.3小学期基础语法记录:结构化绑定与行为树
🙂 std::bind() 结构化绑定
std::bind 是 C++11 引入的一个函数适配器,用于绑定函数的一部分参数或重新排列参数顺序,从而生成一个新的可调用对象(callable object)。它属于头文件 <functional>,与 std::function、std::ref 等一同使用非常灵活。
🧠 本质作用
将一个函数(普通函数、成员函数、函数对象或 lambda)“部分调用”,返回一个新的可调用对象。
🧱 头文件
#include <functional>
✅ 基本语法
auto new_func = std::bind(callable, arg1, arg2, ..., argN);
其中:
callable是原始函数或函数对象;arg1~argN是实际传入参数,可以是具体的值、std::placeholders::_1等占位符;- 返回一个新的函数对象
new_func,可以用类似 lambda 的方式调用。
📌 示例:绑定普通函数
#include <iostream>
#include <functional>
using namespace std;
int add(int a, int b) {
return a + b;
}
int main() {
// 固定第一个参数为 10,返回一个只需要一个参数的函数
auto add10 = bind(add, 10, std::placeholders::_1);
cout << add10(5) << endl; // 输出 15
}
🔍 解释:
std::placeholders::_1表示将来调用时传入的第一个参数;bind(add, 10, _1)意味着调用add(10, x),x 是调用add10(x)时传入的值。
💡 示例:绑定成员函数
#include <iostream>
#include <functional>
using namespace std;
struct Printer {
void print(const string& msg) {
cout << "Printing: " << msg << endl;
}
};
int main() {
Printer p;
// bind 成员函数时,第一个参数要绑定对象或对象指针
auto printer = bind(&Printer::print, &p, std::placeholders::_1);
printer("Hello"); // 输出:Printing: Hello
}
🧩 参数重排序
int sub(int a, int b) {
return a - b;
}
int main() {
auto reverse_sub = bind(sub, std::placeholders::_2, std::placeholders::_1);
cout << reverse_sub(3, 10) << endl; // 结果:7,即sub(10, 3)
}
🤯 结合 std::function
std::function<int(int)> f = bind(add, 100, std::placeholders::_1);
cout << f(23); // 输出 123
📦 与 std::ref 一起用(避免值拷贝)
void update(int& x, int val) {
x += val;
}
int main() {
int a = 10;
auto updater = bind(update, std::ref(a), std::placeholders::_1);
updater(5);
cout << a << endl; // 输出 15
}
不加 std::ref 的话,a 会被按值复制,无法修改原始变量。
🛠️ 用途总结
| 用途 | 示例 |
|---|---|
| 固定部分参数(偏函数应用) | bind(f, 10, _1) |
| 改变参数顺序 | bind(f, _2, _1) |
| 延迟执行 | 把函数变成对象存储、传参,稍后执行 |
与 std::function 配合使用 |
用于泛型容器、回调等 |
与 std::ref 搭配避免拷贝 |
修改引用变量 |
⚠️ 注意事项
- 占位符必须使用
std::placeholders::_1,_2, ...,不能自定义。 - 当使用成员函数时,必须传入对象或对象指针。
std::bind可以实现的功能大多数用 lambda 更直观,C++14 以后建议优先用 lambda。- bind 的参数是值拷贝,除非使用
std::ref。
✅ 何时使用 std::bind?
✅ 适合用 bind 的场景 |
❌ 不推荐用 bind 的场景 |
|---|---|
| 需要在 C++11 中实现部分函数应用 | C++14/17/20 可以直接使用 lambda 更清晰 |
| 将成员函数与某个对象绑定作为回调 | lambda 更容易维护与调试 |
| 参数顺序需要调整 | lambda 更明确可读 |
🔚 总结一句话
std::bind是一个强大但在现代 C++ 中略显冗长的工具,它让你可以把函数变成你想要的“函数”,但 lambda 表达式通常是更现代、更清晰的替代方案。
⏫ 关于std::bind 的高级用法
std::bind 本质上是一个通用的函数适配器,功能覆盖很多高级使用场景。我们来继续深入。
🔍 一、常见高级用法汇总
| 用法类型 | 描述 |
|---|---|
| 部分参数绑定(偏函数应用) | 固定部分参数生成新函数 |
| 参数重排 | 调换调用顺序 |
| 与成员函数 + 对象结合使用 | 将成员函数转为独立函数对象 |
与 std::ref / std::cref |
避免不必要的拷贝(传引用或 const 引用) |
| 返回函数对象 | bind 返回可调用对象,适合配合 STL 算法、回调、事件系统等 |
| 用于多参数函数绑定多个位置 | bind(f, _2, 3, _1, 42) 等,灵活绑定不同参数位置 |
✅ 二、更复杂实例讲解
1️⃣ 绑定多个位置+混合值与占位符
int func(int a, int b, int c, int d) {
return a + b * c - d;
}
auto bound = std::bind(func, std::placeholders::_2, 2, std::placeholders::_1, 10);
int result = bound(3, 4); // 相当于 func(4, 2, 3, 10) → 4 + 2*3 - 10 = 0
2️⃣ 与 STL 算法结合
#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>
bool greater_than(int x, int y) {
return x > y;
}
int main() {
std::vector<int> v = {5, 2, 8, 1};
// 生成一个降序排序函数(固定第一个参数)
std::sort(v.begin(), v.end(), std::bind(greater_than, std::placeholders::_2, std::placeholders::_1));
for (int i : v) std::cout << i << ' '; // 输出:8 5 2 1
}
与
std::sort、std::for_each、std::transform搭配使用时非常灵活。
3️⃣ 与成员函数指针和成员变量搭配
struct Person {
std::string name;
int age;
void sayHi(const std::string& msg) const {
std::cout << name << " says: " << msg << '\n';
}
};
int main() {
Person p{"Alice", 20};
// 调用成员函数
auto say = std::bind(&Person::sayHi, &p, std::placeholders::_1);
say("Hello");
// 访问成员变量(注意返回的是引用)
auto getAge = std::bind(&Person::age, &p);
std::cout << getAge() << std::endl; // 输出 20
}
4️⃣ 配合 std::function 作为通用回调
std::function<void(int)> callback;
void handle_event(int event_id) {
std::cout << "Handling event: " << event_id << std::endl;
}
callback = std::bind(handle_event, std::placeholders::_1);
callback(42);
5️⃣ 配合多线程
#include <thread>
void task(int id, const std::string& name) {
std::cout << "Thread " << id << ": " << name << std::endl;
}
int main() {
std::thread t(std::bind(task, 1, "Worker"));
t.join();
}
🔁 三、std::bind vs lambda 表达式
| 特性 | std::bind |
lambda 表达式 |
|---|---|---|
| 参数顺序重排 | 支持,占位符 _1, _2 |
lambda 更直观但不支持“自动”重排 |
| 绑定成员函数 | 支持 | 支持,但写法更繁琐 |
| 捕获引用 | 需搭配 std::ref |
& 捕获即可 |
| 可读性 | 对初学者不太友好 | lambda 更清晰 |
| 复杂重排/组合 | 优势明显 | 写 lambda 可能冗长 |
🚫 四、std::bind 的几个坑
✅ 传引用时注意引用失效
int a = 10;
auto f = std::bind([](int& x){ x = 99; }, a); // 拷贝了 a,不会修改原变量
f();
std::cout << a << std::endl; // 输出仍是 10
// 正确做法
auto f2 = std::bind([](int& x){ x = 99; }, std::ref(a));
f2();
std::cout << a << std::endl; // 输出 99
✅ 使用成员函数时对象生命周期要注意
auto f = std::bind(&Person::sayHi, Person(), _1); // 临时对象立即销毁
f("hello"); // UB!悬空对象
📚 五、总结
| 分类 | 例子 |
|---|---|
| 普通函数绑定参数 | bind(add, 1, _1) |
| 成员函数绑定对象 | bind(&Class::method, &obj, _1) |
| 成员变量访问器 | bind(&Class::var, &obj) |
| 参数重排 | bind(f, _2, _1) |
| STL算法适配 | sort(v.begin(), v.end(), bind(...)) |
| 多线程任务封装 | thread(bind(func, args...)) |
| 回调函数/事件系统封装 | std::function<void()> cb = bind(...) |
| 延迟执行或惰性计算 | auto lazy = bind(...) |
💠 std::bind 与状态模式
非常好,这是进入 C++ 高阶编程的关键。我们现在讲讲 std::bind 在 状态模式、事件驱动框架、回调系统 等工程应用中的高级封装方式。
这些场景中,bind 最重要的作用是:
把行为(函数)抽象成参数 —— 也就是"函数指针 + 上下文 + 参数封装"。
🧩 一、在状态模式中的应用
✅ 场景描述
状态模式需要对象根据当前状态调用不同处理函数,而且这些函数往往有不同的“上下文”参数。我们希望把这些函数“封装”成行为对象,并在状态转移时统一管理。
🔨 示例:简化状态切换绑定函数
#include <iostream>
#include <functional>
#include <unordered_map>
#include <string>
using namespace std;
class Robot {
public:
void idle() {
cout << "[State] Idle...\n";
}
void walking(int speed) {
cout << "[State] Walking at speed " << speed << "\n";
}
void charging() {
cout << "[State] Charging...\n";
}
};
int main() {
Robot r;
// 将状态行为封装为函数对象
unordered_map<string, function<void()>> stateActions;
stateActions["IDLE"] = bind(&Robot::idle, &r);
stateActions["WALKING"] = bind(&Robot::walking, &r, 5); // 固定 speed
stateActions["CHARGING"] = bind(&Robot::charging, &r);
// 状态调度模拟
vector<string> state_sequence = {"IDLE", "WALKING", "CHARGING"};
for (auto& state : state_sequence) {
stateActions[state](); // 统一调用接口
}
}
✨ 优点:
- 所有行为都封装成
function<void()> - 不需要区分传参方式,也无需用继承或虚函数
bind把成员函数 + 参数“打包”成标准接口
🌀 二、在回调/事件框架中的封装
你可以将 std::bind 用于:
- 网络/IO事件触发时的回调
- UI/交互信号的注册
- 控制器(如ROS)策略行为的回调链
🔔 示例:事件注册系统
#include <iostream>
#include <unordered_map>
#include <functional>
using namespace std;
class EventSystem {
unordered_map<string, function<void(int)>> handlers;
public:
void registerHandler(const string& name, function<void(int)> f) {
handlers[name] = f;
}
void trigger(const string& name, int code) {
if (handlers.count(name)) handlers[name](code);
else cout << "No handler for event: " << name << endl;
}
};
class Controller {
public:
void onSensorEvent(int value) {
cout << "[Sensor] Value: " << value << endl;
}
void onError(int code) {
cout << "[Error] Code: " << code << endl;
}
};
int main() {
EventSystem es;
Controller ctrl;
es.registerHandler("sensor", bind(&Controller::onSensorEvent, &ctrl, std::placeholders::_1));
es.registerHandler("error", bind(&Controller::onError, &ctrl, std::placeholders::_1));
es.trigger("sensor", 42);
es.trigger("error", 404);
}
🧠 深层理解:为什么用 bind 优于裸函数指针?
裸函数指针有几个致命弱点:
- 不支持成员函数(必须是静态函数)
- 无法封装上下文(比如对象引用)
- 不能传参或调整参数格式
- 无法延迟执行、不能组合
而 std::bind 搭配 std::function 做到以下关键特性:
| 能力 | bind 是否支持 | 说明 |
|---|---|---|
| 封装对象上下文 | ✅ | 成员函数自动绑定对象指针 |
| 延迟执行 | ✅ | 不立即调用,只创建 callable |
| 参数预绑定 | ✅ | 比如提前绑定固定值 |
| 占位符支持 | ✅ | _1, _2 等支持动态注入 |
| 统一接口 | ✅ | 封装成 function<sig> |
🚀 三、在行为树 / 机器人策略模块中的应用
比如你在开发决策树/行为树/状态机时,行为节点(比如“转向”、“开火”、“避障”)都可以用 bind 绑定后注册到节点里。
class ActionNode {
public:
function<bool()> action;
ActionNode(function<bool()> func) : action(func) {}
bool execute() {
return action();
}
};
class Agent {
public:
bool move() {
cout << "Moving..." << endl;
return true;
}
bool avoidObstacle() {
cout << "Avoiding obstacle!" << endl;
return false;
}
};
int main() {
Agent agent;
ActionNode moveNode(bind(&Agent::move, &agent));
ActionNode avoidNode(bind(&Agent::avoidObstacle, &agent));
moveNode.execute();
avoidNode.execute();
}
🧾 总结:什么时候应该用 bind 来封装行为?
| 场景类型 | 是否推荐使用 bind |
说明 |
|---|---|---|
| 成员函数注册 | ✅ | 简化对象 + 成员函数调用 |
| 状态模式行为映射 | ✅ | 使用 map<string, function> 管理状态 |
| 回调系统 | ✅ | 参数 + 上下文封装为统一接口 |
| 参数部分绑定 | ✅ | 用于生成简化接口、用于事件系统 |
| C++14 以上 | 🟡 推荐用 lambda | 更简洁,但可读性差时仍推荐 bind |
🏖️ 行为调度器(BehaviorManager)的简单Demo
好的,Eigh18n,我们现在设计一个完整的机器人行为调度系统架构草图,结合你正在学习的“视觉导航”、“策略规划”和“决策树”,我们将重点使用 std::bind 和 std::function 来搭建一个灵活可扩展的行为系统框架,适合后续拓展为行为树、状态机甚至 ROS 行为插件。
🎯 系统目标
设计一个行为调度器(BehaviorManager),可注册多个“行为节点”,每个节点封装一个动作函数(如移动、转向、避障、执行任务等),并支持运行、切换、失败回退等机制。
📐 模块架构图
+-----------------------+
| BehaviorManager | <-- 注册、切换、执行行为节点
+-----------------------+
| + register(name, fn) |
| + run(name) |
+-----------------------+
|
v
+-----------------------+
| BehaviorNode | <-- 每个行为:一个函数对象 + 状态
+-----------------------+
| + execute() -> bool |
+-----------------------+
行为函数由 RobotController 提供
+-----------------------+
| RobotController |
+-----------------------+
| + moveTo(x, y) |
| + avoidObstacle() |
| + followLine() |
+-----------------------+
🧱 Step-by-step 实现
1️⃣ 定义行为节点(BehaviorNode)
#include <functional>
#include <string>
class BehaviorNode {
public:
using Action = std::function<bool()>;
BehaviorNode(Action act, const std::string& name = "")
: action(std::move(act)), node_name(name) {}
bool execute() {
return action(); // 返回 true 表示成功,false 表示失败
}
std::string name() const {
return node_name;
}
private:
Action action;
std::string node_name;
};
2️⃣ 定义行为管理器(BehaviorManager)
#include <unordered_map>
#include <iostream>
class BehaviorManager {
public:
void registerBehavior(const std::string& name, BehaviorNode::Action act) {
behaviors[name] = BehaviorNode(act, name);
}
bool run(const std::string& name) {
if (behaviors.count(name)) {
std::cout << "[Executing] " << name << std::endl;
bool result = behaviors[name].execute();
std::cout << "→ Result: " << (result ? "Success" : "Failure") << "\n";
return result;
}
std::cerr << "Behavior not found: " << name << std::endl;
return false;
}
private:
std::unordered_map<std::string, BehaviorNode> behaviors;
};
3️⃣ 定义机器人控制器(RobotController)
#include <iostream>
class RobotController {
public:
bool moveTo(int x, int y) {
std::cout << "Moving to (" << x << "," << y << ")\n";
return true;
}
bool avoidObstacle() {
std::cout << "Obstacle detected, avoiding...\n";
return true;
}
bool followLine() {
std::cout << "Following line using vision...\n";
return true;
}
bool failExample() {
std::cout << "Trying something... Failed\n";
return false;
}
};
4️⃣ 注册行为(使用 std::bind)
int main() {
RobotController robot;
BehaviorManager bm;
using namespace std::placeholders;
// 注册各种行为
bm.registerBehavior("moveToTarget", std::bind(&RobotController::moveTo, &robot, 10, 20));
bm.registerBehavior("avoid", std::bind(&RobotController::avoidObstacle, &robot));
bm.registerBehavior("lineFollow", std::bind(&RobotController::followLine, &robot));
bm.registerBehavior("failTest", std::bind(&RobotController::failExample, &robot));
// 执行行为
bm.run("lineFollow");
bm.run("moveToTarget");
bm.run("avoid");
bm.run("failTest");
}
✅ 输出示例
[Executing] lineFollow
Following line using vision...
→ Result: Success
[Executing] moveToTarget
Moving to (10,20)
→ Result: Success
[Executing] avoid
Obstacle detected, avoiding...
→ Result: Success
[Executing] failTest
Trying something... Failed
→ Result: Failure
🧠 可扩展性设计方向
| 扩展目标 | 说明 |
|---|---|
| ✅ 状态判断/条件行为 | 可加入 condition 参数:if (check()) then run() |
| ✅ 行为树/序列节点 | 可将多个 BehaviorNode 组合为 SequenceNode/FallbackNode |
| ✅ 行为优先级调度 | 给每个节点设置 priority 字段,设计调度器策略 |
| ✅ 异步/延迟行为执行 | 将 Action 扩展为异步:返回 std::future<bool>、支持超时控制等 |
| ✅ ROS 节点整合 | 用 bind(&Class::ros_callback, this, ...) 注册行为、事件、回调等 |
📘 总结
✅ 你现在拥有了一个完整的、可组合的行为框架基础:
- 用
std::bind灵活注册成员函数(带参数)为标准行为对象; - 用
std::function封装成通用回调格式; - 利用
unordered_map<string, function>构建行为调度系统; - 后续你可以加入状态判断、行为树节点等结构来拓展智能决策系统。
📡 行为树(Behavior Tree)的简单Demo
我们现在在上一节行为系统的基础上,进一步构建一个轻量但完整的行为树(Behavior Tree)框架,适合机器人导航/策略控制/视觉任务调度等场景,注重模块组合、扩展性与执行逻辑清晰。
🧠 本轮目标:构建行为树系统
✅ 功能点:
- 行为节点(ActionNode):封装基本行为(如走路、避障)
- 组合节点(Composite):支持:
- 顺序节点(Sequence):所有子节点都成功才成功
- 选择节点(Selector):任一子节点成功就成功
- 条件节点(Condition):可加入判断逻辑,如视觉检测是否通过
- 返回值标准化:所有节点统一返回
enum class Status { SUCCESS, FAILURE, RUNNING }
🧱 一、通用结构定义
#include <iostream>
#include <functional>
#include <vector>
#include <string>
#include <memory>
enum class Status { SUCCESS, FAILURE, RUNNING };
class BehaviorNode {
public:
virtual ~BehaviorNode() = default;
virtual Status execute() = 0;
virtual std::string name() const = 0;
};
🔨 二、实现基本行为节点(ActionNode)
class ActionNode : public BehaviorNode {
public:
using Action = std::function<Status()>;
ActionNode(std::string n, Action act) : node_name(std::move(n)), action(std::move(act)) {}
Status execute() override {
std::cout << "[Action] " << node_name << "...\n";
return action();
}
std::string name() const override { return node_name; }
private:
std::string node_name;
Action action;
};
🧠 三、实现组合节点(Sequence 和 Selector)
class CompositeNode : public BehaviorNode {
protected:
std::vector<std::shared_ptr<BehaviorNode>> children;
std::string node_name;
public:
CompositeNode(std::string n) : node_name(std::move(n)) {}
void addChild(std::shared_ptr<BehaviorNode> child) {
children.push_back(std::move(child));
}
std::string name() const override { return node_name; }
};
class SequenceNode : public CompositeNode {
public:
using CompositeNode::CompositeNode;
Status execute() override {
std::cout << "[Sequence] " << node_name << " Start\n";
for (auto& child : children) {
Status status = child->execute();
if (status != Status::SUCCESS)
return status;
}
return Status::SUCCESS;
}
};
class SelectorNode : public CompositeNode {
public:
using CompositeNode::CompositeNode;
Status execute() override {
std::cout << "[Selector] " << node_name << " Start\n";
for (auto& child : children) {
Status status = child->execute();
if (status == Status::SUCCESS)
return Status::SUCCESS;
}
return Status::FAILURE;
}
};
🔍 四、实现条件判断节点(ConditionNode)
class ConditionNode : public BehaviorNode {
public:
using Condition = std::function<bool()>;
ConditionNode(std::string n, Condition cond)
: node_name(std::move(n)), condition(std::move(cond)) {}
Status execute() override {
std::cout << "[Condition] " << node_name << "\n";
return condition() ? Status::SUCCESS : Status::FAILURE;
}
std::string name() const override { return node_name; }
private:
std::string node_name;
Condition condition;
};
🤖 五、机器人控制逻辑封装(RobotController)
class RobotController {
public:
bool obstacleDetected = false;
Status moveTo(int x, int y) {
std::cout << "→ Moving to (" << x << "," << y << ")\n";
return Status::SUCCESS;
}
Status avoid() {
std::cout << "→ Avoiding obstacle!\n";
return Status::SUCCESS;
}
Status detectObstacle() {
std::cout << "→ Checking for obstacle: " << (obstacleDetected ? "YES" : "NO") << "\n";
return obstacleDetected ? Status::SUCCESS : Status::FAILURE;
}
};
🧪 六、行为树构建 & 执行
int main() {
RobotController robot;
using namespace std::placeholders;
// 创建节点
auto checkObstacle = std::make_shared<ConditionNode>("IsObstacle",
std::bind(&RobotController::detectObstacle, &robot));
auto avoid = std::make_shared<ActionNode>("Avoid",
std::bind(&RobotController::avoid, &robot));
auto moveToTarget = std::make_shared<ActionNode>("MoveToTarget",
std::bind(&RobotController::moveTo, &robot, 10, 20));
// 构建 Selector(如果有障碍物则避障,否则继续)
auto root = std::make_shared<SelectorNode>("RootSelector");
auto obstacleBranch = std::make_shared<SequenceNode>("ObstacleHandling");
obstacleBranch->addChild(checkObstacle);
obstacleBranch->addChild(avoid);
root->addChild(obstacleBranch);
root->addChild(moveToTarget); // fallback plan
// --- 情况一:无障碍 ---
std::cout << "\n--- No Obstacle ---\n";
robot.obstacleDetected = false;
root->execute();
// --- 情况二:有障碍 ---
std::cout << "\n--- Obstacle Detected ---\n";
robot.obstacleDetected = true;
root->execute();
}
🧾 输出结果示意
--- No Obstacle ---
[Selector] RootSelector Start
[Sequence] ObstacleHandling Start
[Condition] IsObstacle
→ Checking for obstacle: NO
[Action] MoveToTarget...
→ Moving to (10,20)
--- Obstacle Detected ---
[Selector] RootSelector Start
[Sequence] ObstacleHandling Start
[Condition] IsObstacle
→ Checking for obstacle: YES
[Action] Avoid...
→ Avoiding obstacle!
📘 总结设计亮点
| 功能 | 设计说明 |
|---|---|
| 行为封装 | 使用 std::bind 将行为与上下文、参数统一封装为 std::function |
| 统一返回值 | 所有节点返回 Status(支持 SUCCESS / FAILURE / RUNNING) |
| 可组合性强 | Sequence / Selector / Condition 支持任意嵌套组合 |
| 易于拓展 | 可继续添加 LoopNode、ParallelNode、DelayNode 等节点类型 |
🚀 下一步可扩展方向
| 模块 | 功能描述 |
|---|---|
| ✅ 并发节点 | 多个子节点并行执行(适合视觉检测 + 导航同时进行) |
| ✅ Loop 节点 | 持续尝试直到成功 / 失败次数限制 |
| ✅ 黑板(Blackboard) | 用于行为树之间共享信息(如 ROS 的行为树库一样) |
| ✅ 异步行为 | 加入延时/等待/异步检测(返回 RUNNING,状态轮询) |
| ✅ XML 配置解析 | 从 XML/YAML 构建行为树(可以模仿 behavior_tree_cpp_v3) |

浙公网安备 33010602011771号