You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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_;
};
#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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 07:11:10