如何优雅实现参数各异的C++多样化回调?
解决方案
你可以利用C++17及以上的编译期特性,在同一个Foo函数中根据回调的参数签名自动匹配对应的调用逻辑,既复用通用代码,又支持不同参数的回调。
方案一:if constexpr + std::is_invocable_v
通过编译期判断回调是否能接受特定参数,选择对应的调用分支,无运行时开销:
#include <iostream> #include <string> #include <type_traits> class Base{ public: template <typename F> void Foo(int x, int y, F callback) { // 通用逻辑:生成z、w、str(示例逻辑,替换为你的实际代码) int z = x + y; int w = x * y; std::string str = std::to_string(x) + std::to_string(y); // 编译期判断回调签名,选择调用方式 if constexpr (std::is_invocable_v<F>) { callback(); } else if constexpr (std::is_invocable_v<F, int, int>) { callback(z, w); } else if constexpr (std::is_invocable_v<F, const std::string&>) { callback(str); } else { // 编译期报错,提示不支持的回调类型 static_assert(false, "当前回调签名不被支持"); } } }; int main() { Base b; b.Foo(1, 2, [](){ std::cout << "无参数回调\n"; }); b.Foo(1, 2, [](int z, int w){ std::cout << z << " " << w << " 两个整数参数回调\n"; }); b.Foo(1, 2, [](const std::string& str){ std::cout << str.size() << " 字符串参数回调\n"; }); }
方案二:C++20 requires表达式(更直观)
用requires表达式直接检查回调的可调用性,代码可读性更强:
#include <iostream> #include <string> class Base{ private: void invoke_callback(const auto& callback, int z, int w, const std::string& str) { if constexpr (requires { callback(); }) { callback(); } else if constexpr (requires { callback(z, w); }) { callback(z, w); } else if constexpr (requires { callback(str); }) { callback(str); } else { static_assert(false, "当前回调签名不被支持"); } } public: template <typename F> void Foo(int x, int y, F callback) { // 通用逻辑:生成z、w、str int z = x + y; int w = x * y; std::string str = std::to_string(x) + std::to_string(y); invoke_callback(callback, z, w, str); } }; // main函数同方案一
核心优势
- 完全复用通用逻辑,避免重复编写多个几乎相同的函数
- 编译期决议,无额外运行时开销
- 编译期检查回调合法性,提前发现错误
内容的提问来源于stack exchange,提问作者Wayne Tseng
相关产品推荐
相关产品推荐

