C++11 任意多个一元函数的链式调用优化实现

975 阅读2分钟

「这是我参与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();
}

输出结果为

image.png