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

C++泛型Rule类实例化失败求助:支持任意可调用对象

Rule类模板参数推导与执行错误的解决方案

项目背景

开发一个多规则实例系统,每个Rule执行动作后根据Message枚举传递控制权,动作支持任意可调用对象(函数、lambda等),可接受任意参数但必须返回Message。

原始代码

#include <concepts>
#include <map>
#include <unordered_map>
#include <functional>
#include <iostream>

enum Message {
    success,
    failure,
    nothing
};

class BaseRule {};

template <typename F, typename ...Args> requires std::invocable<F, Args...>
class Rule: public BaseRule {
private:
    std::map<Message, BaseRule*> successors;    //a feature allowing to chain rules, unused at this moment
    template <typename... Ts> struct undef;     // for testing purposes only

public:
    F action;

    Rule(F _action) : action(_action) {};
    void addSuccessor(const Message, BaseRule*);
    void removeSuccessor(const Message);

    template <typename... ExecuteArgs>
    void execute(ExecuteArgs&&... args) {
        //execute the action, and call the next rule's action according to the Message returned

    if constexpr (std::is_invocable_v<F, ExecuteArgs...>)
        std::invoke(action, std::forward<ExecuteArgs>(args)...);
    else
        undef<F, ExecuteArgs...> _; // for testing purposes, expected to fail and give me the types of F and ExecuteArgs in the error message
    };

};

class App {
private:
    std::unordered_map<unsigned int, std::function<void()>> boundActions;

public:
    template <typename F, typename ...Args> requires std::invocable<F, Args...>
    void bindInput(const unsigned int _key, BaseRule* _rule, Args... _args) {
        auto callable = [_rule, _args...]() {
            static_cast<Rule<F, Args...>*>(_rule)->execute(_args...);
        };
        boundActions[_key] = callable;
    };

    template <typename F, typename ...Args> requires std::invocable<F, Args...>
    void launch(Rule<F> _startingPoint, Args... args) {
        _startingPoint.execute(args...);
        loop();
    };

    void loop() {
        while (!quit) {
            for (unsigned int i = 0; i < 256; ++i) {
                if (downKeys[i]) {
                    auto it = boundActions.find(i);
                    if (it != boundActions.end())
                        it->second();   // Invoke the stored callable object
                }
            }
        }
    };
};

int main() {
    App app = App();

    //app is captured in the lamba to perform some logic
    Rule init = Rule([&app]() -> Message {
        std::cout << "Initialization" << std::endl;
        return nothing;
    });

    //This fails
    Rule mark = Rule([&app](const unsigned int _target) -> Message {
        std::cout << "Mark" << std::endl;
        return success;
    });

    app.bindInput<decltype(init.action)>(32, &init);
    app.bindInput<decltype(mark.action)>(65, &mark, 0); //Thus, this also fails
    app.bindInput<decltype(mark.action)>(90, &mark, 1); //Same

    app.launch(&init);

    return 0;
}

遇到的问题

  1. 带参数Rule实例化失败:无法从带参数的lambda推导Rule<F>的模板参数,报C2748错误;替换为普通函数或显式指定模板参数时,出现C7602约束不满足错误,仅无参数函数可正常工作。
  2. execute函数编译错误:if constexpr (std::is_invocable_v<F, ExecuteArgs...>)处触发C2100非法间接寻址错误。

问题分析与修复方案

1. 重构Rule类模板参数

原始Rule类模板同时指定F和Args,导致编译器无法自动推导Args(lambda的参数类型是其自身类型的一部分,无需额外指定)。修改为仅用F作为模板参数,将可调用约束移至execute函数,并检查返回值是否为Message:

template <typename F>
class Rule: public BaseRule {
private:
    std::map<Message, BaseRule*> successors;
    template <typename... Ts> struct undef;

public:
    F action;

    Rule(F _action) : action(std::move(_action)) {};
    void addSuccessor(const Message msg, BaseRule* rule) {
        successors[msg] = rule;
    }
    void removeSuccessor(const Message msg) {
        successors.erase(msg);
    }

    template <typename... ExecuteArgs>
    auto execute(ExecuteArgs&&... args) -> Message {
        if constexpr (std::is_invocable_r_v<Message, F, ExecuteArgs...>) {
            Message result = std::invoke(action, std::forward<ExecuteArgs>(args)...);
            // 此处可添加根据result调用后续规则的逻辑
            auto it = successors.find(result);
            if (it != successors.end()) {
                // 示例:假设后续规则无参数,实际可根据需求调整
                static_cast<Rule<F>*>(it->second)->execute();
            }
            return result;
        } else {
            undef<F, ExecuteArgs...> _;
            return nothing; // 仅为编译通过,实际会触发undef的类型错误
        }
    };
};

2. 修正bindInput函数的类型转换

原始bindInput中强制转换Rule<F, Args...>类型错误,改为推导实际的RuleT类型,并用std::apply处理参数转发:

template <typename RuleT, typename ...Args>
requires std::is_base_of_v<BaseRule, RuleT> && 
         std::is_invocable_r_v<Message, decltype(std::declval<RuleT>().action), Args...>
void bindInput(const unsigned int _key, RuleT* _rule, Args... _args) {
    auto callable = [_rule, args = std::make_tuple(std::forward<Args>(_args)...)]() {
        std::apply([_rule](auto&&... inner_args) {
            _rule->execute(std::forward<decltype(inner_args)>(inner_args)...);
        }, args);
    };
    boundActions[_key] = std::move(callable);
}

3. 修复launch函数参数不匹配

原始launch接受Rule<F>值,但传入的是指针,改为接受RuleT引用,并添加约束确保动作可调用:

template <typename RuleT, typename ...Args>
requires std::is_base_of_v<BaseRule, RuleT> &&
         std::is_invocable_r_v<Message, decltype(std::declval<RuleT>().action), Args...>
void launch(RuleT& _startingPoint, Args... args) {
    _startingPoint.execute(std::forward<Args>(args)...);
    loop();
}

4. 补充App类缺失成员

原始代码中loop函数使用的quit和downKeys未定义,需补充:

class App {
private:
    std::unordered_map<unsigned int, std::function<void()>> boundActions;
    bool quit = false;
    bool downKeys[256] = {false};
    // ... 其他函数
};

完整修复后代码

#include <concepts>
#include <map>
#include <unordered_map>
#include <functional>
#include <iostream>
#include <tuple>

enum Message {
    success,
    failure,
    nothing
};

class BaseRule {};

template <typename F>
class Rule: public BaseRule {
private:
    std::map<Message, BaseRule*> successors;
    template <typename... Ts> struct undef;

public:
    F action;

    Rule(F _action) : action(std::move(_action)) {};
    void addSuccessor(const Message msg, BaseRule* rule) {
        successors[msg] = rule;
    }
    void removeSuccessor(const Message msg) {
        successors.erase(msg);
    }

    template <typename... ExecuteArgs>
    auto execute(ExecuteArgs&&... args) -> Message {
        if constexpr (std::is_invocable_r_v<Message, F, ExecuteArgs...>) {
            Message result = std::invoke(action, std::forward<ExecuteArgs>(args)...);
            // 根据返回值调用后续规则示例
            auto it = successors.find(result);
            if (it != successors.end()) {
                // 此处可根据后续规则的参数需求调整execute调用
                static_cast<Rule<F>*>(it->second)->execute();
            }
            return result;
        } else {
            undef<F, ExecuteArgs...> _;
            return nothing;
        }
    };
};

class App {
private:
    std::unordered_map<unsigned int, std::function<void()>> boundActions;
    bool quit = false;
    bool downKeys[256] = {false};

public:
    template <typename RuleT, typename ...Args>
    requires std::is_base_of_v<BaseRule, RuleT> && 
             std::is_invocable_r_v<Message, decltype(std::declval<RuleT>().action), Args...>
    void bindInput(const unsigned int _key, RuleT* _rule, Args... _args) {
        auto callable = [_rule, args = std::make_tuple(std::forward<Args>(_args)...)]() {
            std::apply([_rule](auto&&... inner_args) {
                _rule->execute(std::forward<decltype(inner_args)>(inner_args)...);
            }, args);
        };
        boundActions[_key] = std::move(callable);
    }

    template <typename RuleT, typename ...Args>
    requires std::is_base_of_v<BaseRule, RuleT> &&
             std::is_invocable_r_v<Message, decltype(std::declval<RuleT>().action), Args...>
    void launch(RuleT& _startingPoint, Args... args) {
        _startingPoint.execute(std::forward<Args>(args)...);
        loop();
    }

    void loop() {
        while (!quit) {
            for (unsigned int i = 0; i < 256; ++i) {
                if (downKeys[i]) {
                    auto it = boundActions.find(i);
                    if (it != boundActions.end()) {
                        it->second();
                        downKeys[i] = false; // 避免重复触发
                    }
                }
            }
        }
    }

    // 模拟按键按下的辅助函数
    void pressKey(unsigned int key) {
        if (key < 256) {
            downKeys[key] = true;
        }
    }
};

int main() {
    App app;

    Rule init([&app]() -> Message {
        std::cout << "Initialization" << std::endl;
        return nothing;
    });

    Rule mark([&app](const unsigned int _target) -> Message {
        std::cout << "Mark target: " << _target << std::endl;
        return success;
    });

    app.bindInput(32, &init);
    app.bindInput(65, &mark, 0);
    app.bindInput(90, &mark, 1);

    // 模拟按键触发
    app.pressKey(32);
    app.pressKey(65);
    app.pressKey(90);

    app.launch(init);

    return 0;
}

内容的提问来源于stack exchange,提问作者MetaZenithian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 21:18:13