C++11下如何实现House类存储函数及参数以便后续调用?
解决House类的安全检查实现问题
嘿,我来帮你搞定这个House类的实现问题!先看看你的现有代码里的几个关键问题,再给你一个完全符合C++11标准的正确实现~
你的问题回顾
你有实现了LockCheck()方法的Door类,以及包含BurnerCheck()方法的Stove类,想要实现一个House类:
- 构造函数能接收
Door::LockCheck或Stove::BurnerCheck这类函数,以及对应任意数量的参数 - 存储函数与参数,后续通过
check_safety()调用执行
你给出的示例用法:
auto stove = Stove(); auto stove_check = stove.BurnerCheck; auto burner_args = std::make_tuple<bool, bool>(true, false); auto house = House(burner_args, stove_check); // do some other stuff... house.check_safety(); // 内部调用 stove.BurnerCheck(burner_args)
你的现有实现:
template <typename ReturnType, typename... Args> class House { public: House(Args... args, std::function<ReturnType(Args...)> func) : input_args_(std::forward_as_tuple(args...)), safety_func_(func) {} }; private: Args... input_args_; // Is this the correct declaration? std::function<ReturnType(Args...)> safety_func_; };
现有代码的问题
- 成员变量声明错误:模板参数包
Args...不能直接作为成员变量类型,必须用std::tuple<Args...>来打包存储参数 - 构造函数参数顺序问题:C++11的模板推导对参数包位置有限制,把
Args... args放在前面会导致编译器无法正确推导std::function的类型 - 成员函数处理缺失:你示例中传递的是类的非静态成员函数指针,这类指针必须绑定类实例才能调用(隐含
this指针参数),现有代码没处理这个场景
符合C++11标准的正确实现
我们可以通过绑定可调用对象和参数,把它们转换成一个无参的函数对象存储,这样后续调用check_safety()会非常简洁。另外因为C++11没有std::apply,我们需要自己实现参数展开的辅助函数:
#include <functional> #include <tuple> #include <utility> // C++11没有std::apply,自己实现tuple参数展开的辅助函数 template<typename Func, typename Tuple, size_t... Indices> auto invoke_helper(Func&& func, Tuple&& tuple, std::index_sequence<Indices...>) -> decltype(std::forward<Func>(func)(std::get<Indices>(std::forward<Tuple>(tuple))...)) { return std::forward<Func>(func)(std::get<Indices>(std::forward<Tuple>(tuple))...); } template<typename Func, typename Tuple> auto invoke(Func&& func, Tuple&& tuple) -> decltype(invoke_helper(std::forward<Func>(func), std::forward<Tuple>(tuple), std::make_index_sequence<std::tuple_size<typename std::decay<Tuple>::type>::value>())) { using Indices = std::make_index_sequence<std::tuple_size<typename std::decay<Tuple>::type>::value>; return invoke_helper(std::forward<Func>(func), std::forward<Tuple>(tuple), Indices()); } // House类核心实现 template<typename Func, typename... Args> class House { public: // 构造函数:接收可调用对象和参数,绑定为无参函数 House(Func&& func, Args&&... args) : safety_check_(std::bind( [func = std::forward<Func>(func), args_tuple = std::make_tuple(std::forward<Args>(args)...)]() { invoke(func, args_tuple); } )) {} // 执行安全检查 void check_safety() { safety_check_(); } private: std::function<void()> safety_check_; }; // 辅助推导函数:C++11类模板无法自动推导参数,用这个函数简化对象创建 template<typename Func, typename... Args> House<Func, Args...> make_house(Func&& func, Args&&... args) { return House<Func, Args...>(std::forward<Func>(func), std::forward<Args>(args)...); } // 测试用的Door和Stove类 class Door { public: bool LockCheck(bool is_locked) { return is_locked; } }; class Stove { public: bool BurnerCheck(bool front_burner, bool back_burner) { return !front_burner && !back_burner; } };
用法示例
方式1:先绑定成员函数与实例
int main() { Stove stove; // 用std::bind把Stove实例和成员函数绑定,得到可调用对象 auto stove_check = std::bind(&Stove::BurnerCheck, &stove, std::placeholders::_1, std::placeholders::_2); // 用make_house创建House对象,传入绑定好的函数和参数 auto house = make_house(stove_check, true, false); house.check_safety(); // 内部调用 stove.BurnerCheck(true, false) Door door; auto door_check = std::bind(&Door::LockCheck, &door, std::placeholders::_1); auto house2 = make_house(door_check, true); house2.check_safety(); // 内部调用 door.LockCheck(true) return 0; }
方式2:直接传递成员函数、实例和参数(可选扩展)
如果不想手动写std::bind,可以给House类加一个重载构造函数:
// 在House类中添加针对成员函数的重载构造 template<typename Class, typename ReturnType, typename... MemArgs, typename... Args> House(ReturnType (Class::*mem_func)(MemArgs...), Class* instance, Args&&... args) : safety_check_(std::bind( [mem_func, instance, args_tuple = std::make_tuple(std::forward<Args>(args)...)]() { invoke(std::bind(mem_func, instance), args_tuple); } )) {}
这样使用时更直接:
auto house = make_house(&Stove::BurnerCheck, &stove, true, false); auto house2 = make_house(&Door::LockCheck, &door, true);
实现说明
- 参数打包与展开:用
std::tuple存储参数,通过自定义的invoke函数展开tuple元素作为函数参数 - 可调用对象兼容:支持普通函数、成员函数指针、lambda等任意可调用类型
- C++11兼容性:所有实现都基于C++11标准,没有使用后续版本的特性
内容的提问来源于stack exchange,提问作者BoltzmannBrain
相关产品推荐
相关产品推荐

