「这是我参与11月更文挑战的第 9 天,活动详情查看:2021最后一次更文挑战」。
参加该活动的第 19 篇文章
预备知识点
decltype
是根据变量获得其类型,典型的用法是using type = decltype(a)
。
std::declval
是根据类型获得基变量,典型的用法是auto&& a = std::declval<type>();
。另外,由于std::declval
并没有定义,所以仅能用于模板推导中。
问题与方案
所谓函数的链式调用,就是说 每个函数的返回值作为参数传给下一个函数,直到传到最后一个函数结束 。
举个实际的例子
/// @note 有三个函数
int f(int x), int g(int y), int h(int z)
/// @note 依次调用三个函数
int a,b,c,parm;
a = f(parm);
b = g(a);
c = h(b);
/// @note 等价于这样链式调用
c = h(g(f(parm)));
显然以上这两种方式在使用的时候,不够简洁方便,如果能把这些简单函数进行组合,就可以像简单函数那样去调用了,如下所示。
compose(f,g,h)(parm);
在 C++ 中如何实现这种链式函数的调用呢?我们先分析一下链式调用的特点:
- 都是一元函数;因为返回值要做下个函数的入参,返回值只有一个结果。
- 一元函数的入参和返回值都是同一种类型;因为返回值要做下个函数的入参。
- 按顺序从前往后调用。
如果希望有多个入参的话,则可以考虑将一个结构体作为入参,类似于 f(data_struct)
来实现多个入参的问题。
具体的实现代码如下
#include <iostream>
#include <utility> // std::declval
template <typename OuterFn, typename InnerFn>
class Composed {
public:
explicit Composed(OuterFn outerFn, InnerFn innerFn) :m_outerFn(outerFn), m_innerFn(innerFn) {}
public:
template <typename Arg>
auto operator()(Arg arg) -> decltype(std::declval<OuterFn>()((std::declval<InnerFn>()(std::declval<Arg>())))) {
return m_outerFn(m_innerFn(arg));
}
private:
InnerFn m_innerFn; ///< 该函数对象的返回值作为入参
OuterFn m_outerFn; ///< 接受入参的函数对象
};
/// @note 递归终止
template <typename Function1, typename Function2>
Composed<Function1, Function2> Compose(Function1 f1, Function2 f2) {
return Composed<Function1, Function2>(f1, f2);
}
/// @note 可变参模板函数
template <typename Function1, typename Function2, typename Function3, typename... Functions>
auto Compose(Function1 f1, Function2 f2, Function3 f3, Functions... fs) -> decltype(Compose(Compose(f1, f2), f3, fs...)) {
return Compose(Compose(f1, f2), f3, fs...);
}
测试代码如下
int gt(int x)
{
return x;
}
int ht(int y)
{
return y;
}
void TestCompose()
{
auto f1 = [](int a) {return a + 1; };
auto g1 = [](int b) {return b + 2; };
auto h1 = [](int c) {return c + 3; };
auto I1 = [](int d) {return d + 4; };
auto J1 = [](int e) {return e + 5; };
auto ret = Compose(f1, g1, h1)(3); ///< 3+1+2+3
std::cout << "Compose0: " << ret << std::endl;
ret = Compose(f1, g1, h1, I1)(3); ///< 3+1+2+3+4
std::cout << "Compose1: " << ret << std::endl;
ret = Compose(f1, g1, h1, I1, J1)(3); ///< 3+1+2+3+4+5
std::cout << "Compose2: " << ret << std::endl;
ret = Compose(f1, g1, h1, I1, J1, J1, J1)(3); ///< 3+1+2+3+4+5+5+5
std::cout << "Compose3: " << ret << std::endl;
ret = Compose([](int d) {return d + 4; }, [](int d) {return d + 5; })(3); ///< 3+4+5
std::cout << "Compose4: " << ret << std::endl;
}
int main() {
TestCompose();
}
输出结果为