能否从基类函数参数推导模板参数?优化回调包装器写法
如何从基类虚函数自动推导Callback模板的参数?
完全可以实现!咱们核心思路就是用模板元编程自动提取基类里onFinish虚函数的参数类型,再也不用手动写那些冗长的模板参数了。
先回顾下你现有的代码:
#include <optional> #include <vector> #include <tuple> #include <future> // 你的回调基类 class CallbackFilterProducts { public: virtual ~CallbackFilterProducts() {} virtual void onFinish(const std::optional<class Page>& page, const std::vector<class Product>& products) = 0; }; // 原Callback模板(需要手动指定参数) template <typename T, typename... Args> struct Callback : T { Callback() {} void onFinish(Args&&... args) override { _promise.set_value(std::make_tuple(std::forward<Args>(args)...)); } auto&& promise() { return _promise; } private: std::promise<std::tuple<std::remove_const_t<std::remove_reference_t<Args>>...>> _promise; };
之前你必须这么写,参数列表特别长:
auto callback = std::make_shared<Callback<CallbackFilterProducts, const std::optional<Page>&, const std::vector<Product>&>>();
接下来咱们一步步改造,实现你想要的简化写法:
第一步:用模板特化自动提取函数参数
我们可以写一个带偏特化的辅助模板,专门解析类中onFinish函数的签名:
// 辅助模板:用来提取成员函数的参数、返回值等信息 template<typename Func> struct FuncTraits; // 偏特化:匹配返回值为void、参数为Args...的非const成员函数 template<typename Class, typename... Args> struct FuncTraits<void(Class::*)(Args...)> { using PromiseType = std::promise<std::tuple<std::remove_const_t<std::remove_reference_t<Args>>...>>; // 把原函数的参数类型暴露出来 using FuncSignature = void(Class::*)(Args...); }; // 额外特化:处理const版本的onFinish函数(如果需要的话) template<typename Class, typename... Args> struct FuncTraits<void(Class::*)(Args...) const> { using PromiseType = std::promise<std::tuple<std::remove_const_t<std::remove_reference_t<Args>>...>>; using FuncSignature = void(Class::*)(Args...) const; }; // 主模板:自动匹配基类的onFinish函数 template<typename T, typename Func = decltype(&T::onFinish)> struct CallbackImpl; // 偏特化:根据onFinish的签名生成对应的实现 template<typename T, typename... Args> struct CallbackImpl<T, void(T::*)(Args...)> : T { void onFinish(Args... args) override { _promise.set_value(std::make_tuple(std::forward<Args>(args)...)); } auto&& promise() { return _promise; } private: typename FuncTraits<void(T::*)(Args...)>::PromiseType _promise; }; // 对应const版本的实现 template<typename T, typename... Args> struct CallbackImpl<T, void(T::*)(Args...) const> : T { void onFinish(Args... args) const override { _promise.set_value(std::make_tuple(std::forward<Args>(args)...)); } auto&& promise() const { return _promise; } private: mutable typename FuncTraits<void(T::*)(Args...) const>::PromiseType _promise; };
第二步:定义简化的别名模板
最后咱们给CallbackImpl套一个简单的别名,这样用起来就和你想要的一样了:
template<typename T> using Callback = CallbackImpl<T>;
现在可以直接用简化写法了!
改造完成后,你只需要一行代码就能创建回调对象,编译器会自动帮你推导所有参数:
auto callback = std::make_shared<Callback<CallbackFilterProducts>>(); auto future = callback->promise().get_future();
一些补充说明
- 这个方案兼容C++11及以上版本,主流编译器(GCC、Clang、MSVC)都支持。
- 如果你的基类里有多个重载的
onFinish函数,decltype(&T::onFinish)会产生歧义,这时候你需要手动指定要匹配的函数签名(比如用强制类型转换)。 - 如果
onFinish是const成员函数,咱们的特化版本也能正常处理,只需要把promise设为mutable,因为const成员函数不能修改非mutable成员。
这样改造后,代码简洁多了,完全满足你的需求!
内容的提问来源于stack exchange,提问作者anatol
相关产品推荐
相关产品推荐

