首页 > 解决方案 > lambda 参数的模板推导

问题描述

我目前有许多数据访问器,例如:

template <typename Fn>
typename std::result_of<Fn(DataA)>::type GetDataA(Fn fn) {
   DataA a = DoSomeWorkToGetA();
   return fn(a);
}

template <typename Fn>
typename std::result_of<Fn(DataB)>::type GetDataB(Fn fn) {
   DataB b = DoSomeWorkToGetB();
   return fn(b);
}

使用如下:

auto computation_result_a = GetDataA([](DataA a) {
   return DoCustomProcessingOnA(a);
});

auto computation_result_b = GetDataB([](DataB a) {
   return DoCustomProcessingOnB(b);
});

我想要的是一种自动生成这些 Getter 组合的方法,例如:

template <typename Fn>
typename std::result_of<Fn(DataA, DataB)>::type GetDataAandB(Fn fn) {
   DataA a = GetDataA([](DataA a) { return a; });
   DataB b = GetDataB([](DataB b) { return b; });
   return fn(a, b);
}

我在过去看到过这种类型的事情,用户 API 如下:

auto computation_result_a = GetData<A>([](Data a) { /**/ });
auto computation_result_b = GetData<A>([](Data b) { /**/ });
auto computation_result_ab = GetData<A,B>([](Data a, Data b) { /**/ });

但我不确定如何做到这一点。

标签: c++

解决方案


首先,您需要从类型(或标签)到正确DoSomeWorkToGet*功能的映射,例如:

template<typename T> /* or an enum instead of typename */
void DoSomeWorkToGet() = delete;

template<>
auto DoSomeWorkToGet<A>() { return DoSomeWorkToGetA(); }

template<>
auto DoSomeWorkToGet<B>() { return DoSomeWorkToGetB(); }

然后编写函数只是使用可变参数模板和包扩展的问题:

template<typename... Ts, typename F> /* or an enum instead of the first typename */
auto GetData(F f) {
    return f(DoSomeWorkToGet<Ts>()...);
}

用作例如:

auto computation_result_ab = GetData<A, B>([](auto a, auto b) {
    return /* something with a and b */;
});

如果您需要DoSomeWorkToGet按从左到右的顺序评估函数,则不能使用上述函数,但可以使用以下函数:

template<typename... Ts, typename F> /* or an enum instead of the first typename */
auto GetData(F f) {
    using tuple_type = std::tuple<decltype(DoSomeWorkToGet<Ts>())&&...>;
    return std::apply(f, tuple_type{DoSomeWorkToGet<Ts>()...});
}

这需要 C++17。也可以使用 C++11/C++14 实现,但需要做更多的工作(实现std::apply或更弱的形式)。它保证了评估顺序,因为列表初始化(在 中使用tuple_type{...})始终严格按照从左到右的顺序排列。


推荐阅读