C++菜单历史记录器(HistoryKeeper)类实现错误求助
问题分析与解决方案
1. 解决"invalid use of void expression"错误
你直接传Menu::displayMenu(menu_items)给AddAndExecute时,这个调用本身返回void,而函数参数不能接收void类型的值。正确做法是把菜单调用逻辑打包成一个可调用对象(比如lambda表达式),传递给AddAndExecute,而非直接执行它。
2. 实现通用模板版的AddAndExecute
模板方法可以接收任意类型的可调用对象(函数、lambda、bind表达式等),同时将该对象存入历史记录,后续执行"返回"操作时可重新调用。
完整代码示例
HistoryKeeper单例类实现
#include <vector> #include <functional> #include <memory> class HistoryKeeper { public: // 获取单例实例(C++11及以上线程安全) static HistoryKeeper& getInstance() { static HistoryKeeper instance; return instance; } // 通用模板版AddAndExecute:接收任意可调用对象 template<typename Callable> void AddAndExecute(Callable&& func) { // 将可调用对象存入历史,用std::function<void()>统一包装 history_.emplace_back(std::forward<Callable>(func)); // 执行传入的逻辑 func(); } // 返回上一级菜单 void goBack() { if (history_.size() < 2) { // 无可用返回历史 return; } // 移除当前菜单记录 history_.pop_back(); // 执行上一个菜单逻辑 history_.back()(); } // 禁止拷贝与赋值 HistoryKeeper(const HistoryKeeper&) = delete; HistoryKeeper& operator=(const HistoryKeeper&) = delete; private: HistoryKeeper() = default; // 存储历史菜单的可调用对象 std::vector<std::function<void()>> history_; };
Menu类示例(保持你的displayMenu逻辑)
#include <map> #include <iostream> #include <string> #include <cstdlib> class Menu { public: static void displayMenu(const std::map<std::string, std::function<void()>>& menuItems) { while (true) { std::cout << "\n菜单选项:\n"; for (const auto& item : menuItems) { std::cout << "- " << item.first << "\n"; } std::cout << "- 返回\n"; std::string choice; std::cout << "请选择: "; std::cin >> choice; if (choice == "返回") { HistoryKeeper::getInstance().goBack(); break; } auto it = menuItems.find(choice); if (it != menuItems.end()) { it->second(); } else { std::cout << "无效选项,请重新选择\n"; } } } };
调用示例
void subMenu1() { std::map<std::string, std::function<void()>> subItems = { {"选项1", [](){ std::cout << "执行子菜单选项1\n"; }}, {"选项2", [](){ std::cout << "执行子菜单选项2\n"; }} }; // 用lambda打包displayMenu调用,避免void表达式错误 HistoryKeeper::getInstance().AddAndExecute([=](){ Menu::displayMenu(subItems); }); } int main() { std::map<std::string, std::function<void()>> mainItems = { {"子菜单1", subMenu1}, {"退出", [](){ std::cout << "程序退出\n"; std::exit(0); }} }; // 主菜单加入历史并执行 HistoryKeeper::getInstance().AddAndExecute([=](){ Menu::displayMenu(mainItems); }); return 0; }
关键说明
- 规避void表达式错误:通过lambda把
Menu::displayMenu(menu_items)包装成无参数、返回void的可调用对象,确保传递给AddAndExecute的是一个合法的参数。 - 通用模板设计:模板参数
Callable配合std::forward实现完美转发,支持任意可调用类型,存入历史时用std::function<void()>统一封装,简化后续调用逻辑。 - 单例可靠性:使用静态局部变量实现单例,C++11及以上标准保证其初始化的线程安全性,无需额外锁机制。
内容的提问来源于stack exchange,提问作者Memphis Rosewell
相关产品推荐
相关产品推荐

