如何实现两个泛型回调的参数在指定函数中汇合?
可行的实现方案
当然有可行的实现方式!核心思路是类型擦除:当第一个回调被调用时,捕获其值并生成一个等待第二个回调的泛型处理函数,等第二个回调触发时,再把两个值一起传给meet_point。下面分两种场景给出实现:
基础版本(假设foo先于bar调用)
如果你的业务场景中可以保证foo一定先被调用,那么实现非常简洁,利用C++20的泛型lambda和std::function<void(auto)>即可:
#include <functional> #include <utility> #include <iostream> template <class FooType, class BarType> void meet_point(FooType foo_value, BarType bar_value) { // 这里写你的汇合逻辑,示例打印两个值 std::cout << "汇合成功:Foo = " << foo_value << ", Bar = " << bar_value << std::endl; } struct test { // 存储等待bar调用的回调:接收任意类型的bar值 std::function<void(auto)> pending_bar_handler; template <class FooType> void foo(FooType value) { // 捕获foo的值,生成泛型lambda等待bar的调用 pending_bar_handler = [value = std::move(value)](auto bar_value) { meet_point(std::move(value), std::move(bar_value)); }; } template <class BarType> void bar(BarType value) { if (pending_bar_handler) { pending_bar_handler(std::move(value)); // 调用后重置,避免重复触发 pending_bar_handler = nullptr; } } }; template <class F> void sync_request(F&& f) { f("foobar"); } int main() { test t; sync_request([&t](auto value) { t.foo(value); }); t.bar(42); // 触发meet_point("foobar", 42) }
完善版本(支持任意调用顺序)
如果foo和bar的调用顺序不确定,我们可以用std::variant管理状态,不管哪个回调先触发,都能保存对应的等待逻辑:
#include <functional> #include <utility> #include <variant> #include <iostream> template <class FooType, class BarType> void meet_point(FooType foo_value, BarType bar_value) { std::cout << "汇合成功:Foo = " << foo_value << ", Bar = " << bar_value << std::endl; } struct test { // 状态:无等待 / 已接收foo等待bar / 已接收bar等待foo std::variant<std::monostate, std::function<void(auto)>, std::function<void(auto)>> state; template <class FooType> void foo(FooType value) { std::visit([value = std::move(value)](auto& current_state) { using StateType = std::decay_t<decltype(current_state)>; if constexpr (std::is_same_v<StateType, std::monostate>) { // 未收到任何值,保存等待bar的回调 current_state = [value = std::move(value)](auto bar_val) { meet_point(std::move(value), std::move(bar_val)); }; } else if constexpr (std::is_same_v<StateType, std::function<void(auto)>>) { // 已收到bar的等待回调,直接调用(传入foo的值) current_state(std::move(value)); // 重置状态 current_state = std::monostate{}; } }, state); } template <class BarType> void bar(BarType value) { std::visit([value = std::move(value)](auto& current_state) { using StateType = std::decay_t<decltype(current_state)>; if constexpr (std::is_same_v<StateType, std::monostate>) { // 未收到任何值,保存等待foo的回调 current_state = [value = std::move(value)](auto foo_val) { meet_point(std::move(foo_val), std::move(value)); }; } else if constexpr (std::is_same_v<StateType, std::function<void(auto)>>) { // 已收到foo的等待回调,直接调用(传入bar的值) current_state(std::move(value)); // 重置状态 current_state = std::monostate{}; } }, state); } }; template <class F> void sync_request(F&& f) { f("foobar"); } int main() { test t; // 先调用bar,再调用foo,同样能触发汇合 t.bar(42); sync_request([&t](auto value) { t.foo(value); }); }
关键原理说明
- 泛型lambda:C++14及以后支持泛型lambda(参数用
auto),它可以适配任意类型的参数,完美匹配我们不确定的回调值类型。 - 类型擦除:C++20中
std::function支持存储带auto参数的泛型lambda,实现了对具体类型的擦除——我们不需要提前知道foo和bar的参数类型,只需要在调用时动态绑定。 - 状态管理:用
std::variant可以清晰管理"等待foo"、"等待bar"、"无等待"三种状态,确保不管哪个回调先触发,都能正确汇合。
如果你使用C17或更早版本,std::function不支持泛型lambda的auto参数,这时候需要手动实现更复杂的类型擦除(比如自定义基类+模板子类),或者依赖第三方库(如Boost.Function)。不过C20的方案已经足够简洁且覆盖大多数场景。
内容的提问来源于stack exchange,提问作者Vladimir Talybin
相关产品推荐
相关产品推荐

