C++原生无反射,需手动构建字符串到可调用对象的映射表;核心是模板自动绑定成员函数、分签名类型存储映射、谨慎使用宏注册。

为什么 C++ 原生没有反射,却还要手写映射表
C++ 标准至今不支持运行时类型信息(RTTI)级别的成员函数反射,typeid 和 dynamic_cast 仅限多态类型识别,无法枚举类成员或按名字调用函数。所以“实现反射”本质是**人工构造一张从字符串到可调用对象的映射表**——不是魔法,是注册+查找+转发。真正难点不在语法,而在如何让注册过程足够轻量、类型安全,且不手动重复写 std::bind 或 lambda。
用模板推导自动绑定成员函数,避免手动写 std::bind
手动对每个成员函数写 std::bind(&MyClass::func, obj, std::placeholders::_1) 极易出错:参数数量错、占位符漏写、const 限定不匹配都会导致编译失败。更稳的方式是用可变参数模板 + decltype 推导签名,封装成一个 bind_member 工具函数:
template<typename Class, typename Ret, typename... Args>
auto bind_member(Ret (Class::*func)(Args...), Class* obj) {
return [obj, func](Args... args) -> Ret { return (obj->*func)(std::forward<Args>(args)...); };
}
// 支持 const 成员函数需另加重载(否则编译报错)
template<typename Class, typename Ret, typename... Args>
auto bind_member(Ret (Class::*func)(Args...) const, const Class* obj) {
return [obj, func](Args... args) -> Ret { return (obj->*func)(std::forward<Args>(args)...); };
}- 必须显式区分 non-const / const 成员函数,否则模板无法匹配
func类型 - 捕获
obj用值还是引用?若绑定的是栈对象指针,用引用捕获([&obj, func])更安全;但要注意生命周期——这是最容易 crash 的点 - 返回 lambda 而非
std::function,避免类型擦除开销;若需统一存储,再包一层std::function<Ret(Args...)>
映射表怎么存:unordered_map<string, std::function<...>> 的陷阱
常见做法是用 std::unordered_map<std::string, std::function<void()>> 存函数,但实际中函数签名千差万别(返回值不同、参数个数不同),不能全塞进同一个 std::function<void()>。正确做法是:
- 按用途分表:比如
method_map<int(std::string)>存所有返回int、接受一个std::string的方法 - 或者用
std::any+ 运行时类型检查(C++17 起),但会牺牲性能和调试友好性 - 更实用的折中:定义统一接口如
std::function<std::any(const std::vector<std::any>&)>,在调用前做参数数量/类型的 run-time 校验(错误信息可含具体函数名和期望签名) -
std::unordered_map插入时注意 key 重复:同名函数覆盖无声发生,建议插入前加if (map.find(name) != map.end()) throw std::runtime_error("duplicate method: " + name);
注册宏简化写法,但别滥用 __FUNCTION__
手动注册太繁琐,可用宏隐藏模板实例化细节:
立即学习“C++免费学习笔记(深入)”;
#define REFLECT_METHOD(cls, meth) \
map[#meth] = bind_member(&cls::meth, this);但切忌用 __FUNCTION__ 或 __func__ 自动取名——它们返回的是带类作用域的完整符号(如 "MyClass::do_something"),而你想要的是 "do_something"。要么手动传字符串字面量,要么用宏配合字符串化技巧(如 #meth),否则查表永远失败。
另外,宏注册只适用于类内成员函数绑定;静态函数、自由函数需单独注册入口,且无法访问 this ——这点常被忽略,结果调用时报 segmentation fault 却找不到原因。


















