std::invoke_result_t编译错误及CallbackList模板问题求助
C++ CallbackList模板类编译错误排查
1. "std::invoke_result中无type类型" 错误原因与解决
这个错误通常是call()函数的SFINAE约束逻辑有误,导致编译器尝试实例化不兼容的代码分支,或是std::invoke_result的模板参数传递顺序错误。
核心问题分析
你用std::function<void(int, float, std::string)>实例化类后,单独的static_assert能通过,说明std::invoke_result本身可正确推导返回值,但call()函数的模板实现可能没通过SFINAE正确排除无效分支:
- 可能同时定义了有返回值和无返回值的
call()重载,但未用std::enable_if或C++20的requires明确约束,导致编译器尝试实例化两个分支,其中一个分支因返回值为void触发错误; - 或是
std::invoke_result的参数顺序写反(正确顺序为std::invoke_result<可调用对象类型, 参数类型...>)。
修复示例
用SFINAE明确区分两个分支,确保编译器只实例化符合条件的版本:
#include <vector> #include <functional> #include <type_traits> #include <utility> template <typename CallbackType> class CallbackList { private: std::vector<CallbackType> callbacks_; public: void register_callback(CallbackType cb) { callbacks_.push_back(std::move(cb)); } // 有返回值的分支 template <typename... Args> auto call(Args&&... args) -> std::enable_if_t< !std::is_void_v<std::invoke_result_t<CallbackType, Args...>>, std::invoke_result_t<CallbackType, Args...>> { using ResultType = std::invoke_result_t<CallbackType, Args...>; ResultType result; for (auto& cb : callbacks_) { result = std::invoke(cb, std::forward<Args>(args)...); } return result; } // 无返回值的分支 template <typename... Args> std::enable_if_t< std::is_void_v<std::invoke_result_t<CallbackType, Args...>>> call(Args&&... args) { for (auto& cb : callbacks_) { std::invoke(cb, std::forward<Args>(args)...); } } }; // 测试代码 int main() { CallbackList<std::function<void(int, float, std::string)>> list; list.register_callback([](int a, float b, std::string c) { // 无返回值逻辑 }); list.call(1, 3.14f, "test"); return 0; }
2. "function cannot return function type" 错误原因与解决
这个错误是因为修改模板参数时,错误地将函数类型作为返回值参数传递给std::function,而std::function不支持返回函数类型(仅支持返回函数指针或其他可复制构造的类型)。
核心问题分析
比如你可能写成了这类错误的模板定义:
// 错误示例1:模板参数为函数类型,导致R被推导为函数类型 template <typename Func> class CallbackList { using CallbackType = std::function<Func>; // 若Func是嵌套函数类型会直接非法 }; // 错误示例2:拆分模板参数时,R被指定为函数类型 template <typename R, typename... Args> class CallbackList { // 若R是void(int),则std::function<R(Args...)>变成std::function<void(int)(float)>,非法 };
修复示例
明确将模板参数拆分为返回值类型和参数类型列表,避免传递函数类型作为返回值:
#include <vector> #include <functional> #include <type_traits> #include <utility> template <typename R, typename... Args> class CallbackList { private: using CallbackType = std::function<R(Args...)>; std::vector<CallbackType> callbacks_; public: void register_callback(CallbackType cb) { callbacks_.push_back(std::move(cb)); } // 有返回值分支 template <typename... CallArgs> std::enable_if_t<!std::is_void_v<R>, R> call(CallArgs&&... args) { R result; for (auto& cb : callbacks_) { result = std::invoke(cb, std::forward<CallArgs>(args)...); } return result; } // 无返回值分支 template <typename... CallArgs> std::enable_if_t<std::is_void_v<R>> call(CallArgs&&... args) { for (auto& cb : callbacks_) { std::invoke(cb, std::forward<CallArgs>(args)...); } } }; // 测试代码 int main() { // 明确指定返回值为void,参数为int、float、std::string CallbackList<void, int, float, std::string> list; list.register_callback([](int a, float b, std::string c) { // 无返回值逻辑 }); list.call(1, 3.14f, "test"); return 0; }
如果需要支持返回函数指针的回调,只需将R指定为函数指针类型即可,比如CallbackList<void(*)(int), float, std::string>。
内容的提问来源于stack exchange,提问作者Kartik Aiyer
相关产品推荐
相关产品推荐

