基于C++模板根据可调用对象返回值实现DoInvoke分支逻辑
实现可调用对象返回值类型的自动适配
要让MakeEvent根据传入可调用对象的返回值类型,自动让DoInvoke执行对应逻辑,我们可以用C++17的if constexpr做编译期分支判断,配合自定义类型特征来识别std::future类型。下面是完整的可运行代码:
#include <future> #include <functional> #include <type_traits> // 自定义类型特征:判断一个类型是不是std::future的特化 template <typename T> struct is_future : std::false_type {}; template <typename T> struct is_future<std::future<T>> : std::true_type {}; // 便捷常量,简化类型判断 template <typename T> constexpr bool is_future_v = is_future<T>::value; class EventImpl { public: virtual ~EventImpl() = default; std::future<void> Invoke() { return DoInvoke(); } private: virtual std::future<void> DoInvoke() = 0; }; template <typename... Ts> EventImpl* MakeEvent(Ts&&... args) { // 推导bind后可调用对象的返回类型 using BoundFunc = decltype(std::bind(std::forward<Ts>(args)...)); using ResultType = std::invoke_result_t<BoundFunc>; class EventMemberImpl : public EventImpl { public: EventMemberImpl(Ts&&... args) : m_function(std::bind(std::forward<Ts>(args)...)) {} protected: virtual ~EventMemberImpl() {} private: std::future<void> DoInvoke() override { if constexpr (is_future_v<ResultType>) { // 如果可调用对象返回std::future,直接调用并转换为std::future<void> auto task_future = m_function(); // 包装一下,确保原future的任务完成后才返回 return std::async(std::launch::deferred, [fut = std::move(task_future)]() mutable { fut.get(); }); } else { // 如果返回普通类型或void,执行后返回空的std::future<void> m_function(); return std::future<void>(); } } // 用推导出来的返回类型定义std::function,适配所有情况 std::function<ResultType()> m_function; }; return new EventMemberImpl(std::forward<Ts>(args)...); } // 测试用例 int foo() { return 5; } std::future<int> baz() { return std::async(std::launch::async, []() { return 42; }); } void bar() {} int main() { EventImpl* event1 = MakeEvent(&foo); EventImpl* event2 = MakeEvent(&baz); EventImpl* event3 = MakeEvent(&bar); // 执行所有事件并等待完成 event1->Invoke().get(); event2->Invoke().get(); event3->Invoke().get(); // 记得释放内存 delete event1; delete event2; delete event3; return 0; }
核心逻辑说明
类型特征
is_future:
我们写了一个简单的模板结构体,用来识别某个类型是不是std::future的特化版本。通过部分特化,当传入的类型是std::future<T>时,这个特征的value会是true,其他情况则为false。is_future_v是个便捷常量,让我们在代码里不用写::value,更简洁。返回类型推导:
在MakeEvent里,我们先推导std::bind之后得到的可调用对象类型,再用std::invoke_result_t获取它的返回类型ResultType——这一步是为了后续判断可调用对象到底返回什么。编译期分支判断:
用if constexpr(C++17开始支持)在编译时就确定走哪个分支:- 如果返回类型是
std::future,就调用可调用对象得到future,然后把它包装成std::future<void>(这样才能符合DoInvoke的返回类型要求),同时确保原future的任务会被等待完成。 - 如果是普通类型(比如
int)或者void,就直接执行可调用对象,然后返回一个空的std::future<void>。
- 如果返回类型是
通用的
std::function定义:
我们用推导出来的ResultType定义m_function的类型,这样不管可调用对象返回什么,std::function都能正确适配,不用写两个不同的成员变量。
兼容旧版本C++的提示
如果需要支持C11或C14(没有if constexpr),可以用SFINAE和模板特化来实现两个不同的EventMemberImpl版本,分别处理返回future和非future的情况。不过C++17及以上用if constexpr会更简洁直观。
内容的提问来源于stack exchange,提问作者kronat
相关产品推荐
相关产品推荐

