这次要讲的是:C++11如何通过组合函数来简化我们的程序。关于组合函数,大家可能对这个概念有点陌生。组合函数是将N个一元函数组成一种更复杂的函数,每个函数的返回值作为参数传给下一个函数,直到传到最后一个函数结束。这种组合函数的能力可以使我们以一种更直观的方式去完成复杂的链式执行行为。例如有三个函数:

int f(int x), int g(int y), int h(int z)
依次调用三个函数
int a,b,c,parm;
a = f(parm);
b = g(a);
c = h(b);
等价于 c = h(g(f(parm)));

  这两种方式在使用起来不够简洁方便,如果能把这些简单函数组合起来,就可以按简单函数的方式去调用了,更加直观和简洁。比如像这样调用:

compose(f,g,h)(parm);

  这种方式调用不是更方便吗,这种方式把这些函数串在一起了,内部是一个接一个调用并得到最终结果。

在c++中如何实现这种组合函数的调用呢?想想我们应该怎么做吧。我们先分析一下这种组合函数的调用的特点:

  • 都是一元函数;因为返回值要做下个函数的入参,返回值只有一个结果。
  • 一元函数的入参和返回值都是同一种类型;因为返回值要做下个函数的入参。
  • 按顺序从前往后调用。

  通过上面的分析我们知道这种组合函数有个隐含的约束就是,返回值和入参必须相同,这也导致这些函数只能是一元函数。
如果希望有多个入参,则要通过变通的方式了,比如可以将一个结构体作为入参,类似于data_struct f(data_struct)来实现多个入参的问题。

  好了现在看看c++中是如何实现这种调用的吧。

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(declval<OuterFn>()((declval<InnerFn>()(declval<Arg>()))))
{
return m_outerFn(m_innerFn(arg));
} private:
InnerFn m_innerFn;
OuterFn m_outerFn;
}; template <typename Function1, typename Function2>
Composed<Function1, Function2> Compose(Function1 f1, Function2 f2)
{
return Composed<Function1, Function2>(f1, f2);
} 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 + ; };
auto g1 = [](int b){return b + ; };
auto h1 = [](int c){return c + ; };
auto I1 = [](int d){return d + ; };
auto J1 = [](int e){return e + ; }; auto ret = Compose(f1, g1, h1)();
ret = Compose(f1, g1, h1, I1)();
ret = Compose(f1, g1, h1, I1, J1)();
ret = Compose(f1, g1, h1, I1, J1, J1, J1)();
ret = Compose([](int d){return d + ; }, [](int d){return d + ; })(); }

  通过测试程序我们可以看到,我们可以组合任意多个一元函数,这些一元函数可以是function也可以是lamda,它们之间彼此独立没有关联关系。这种组合是非常灵活的,也可以动态调整的。也许有人要问,这个东西有啥用啊,细想一下,它还是挺有价值的:

首先,它比原来的调用更加直观和简洁,其次它可以很方便的实现链式的函数调用,说到链式的函数调用,在做数据处理的时候比较有用,有可能需要对数据进行层层预处理,这些处理过程通过组合方式很容易实现,而且可以方便的增加或者减少处理函数,以及调换顺序,这是非常灵活的。

其次,它可以很容易做成责任链模式,它比动态多态实现的责任链模式更加强大,这个链条可以动态调整,调用函数之间彼此可以没有任何关系,没有继承这种强约束关系,使得我们可以灵活的实现责任链模式。我相信它的价值还有更多。

c++11 boost技术交流群:296561497,欢迎大家来交流技术。

(原创)C++11改进我们的程序之简化我们的程序(二)的更多相关文章

  1. (原创)C++11改进我们的程序之简化我们的程序(八)

    本次要讲的是如何通过泛型函数来简化我们的程序. 泛型函数除了之前介绍的一些优点外还有两个重要的优点 1.消除重复逻辑,提高程序的内聚性和健壮性 泛型函数在某种程度上用来弥补泛型类型的不足.通过泛型类型 ...

  2. C++11改进我们的程序之简化我们的程序1

    C++11改进我们的程序之简化我们的程序(一) C++11在很多方面可以简化我们的程序开发,我会在“简化我们的程序”这一系列的博文中一一讲到,敬请关注.这次要讲的是:C++11如何通过获取函数模板的返 ...

  3. (原创)C++11改进我们的程序之简化我们的程序(四)

    这次要讲的是:c++11统一初始化.统一begin()/end()和for-loop循环如何简化我们的程序 初始化列表 c++11之前有各种各样的初始化语法,有时候初始化的时候还挺麻烦,比较典型的如v ...

  4. (原创)C++11改进我们的程序之简化我们的程序(三)

    这次要讲的是:C++11如何通过auto.decltype和返回值后置来简化我们的程序. auto和c#中的var类似,都是在初始化时自动推断出数据类型.当某个变量的返回值难于书写时,或者不太确定返回 ...

  5. (原创)C++11改进我们的程序之简化我们的程序(一)

    C++11在很多方面可以简化我们的程序开发,我会在“简化我们的程序”这一系列的博文中一一讲到,敬请关注.这次要讲的是:C++11如何通过获取函数模板的返回值类型来简化我们的程序.在谈到简化之前,我们先 ...

  6. (原创)c++11改进我们的模式之改进代理模式,实现通用的AOP框架

    c++11 boost技术交流群:296561497,欢迎大家来交流技术. 本次要讲的时候如何改进代理模式,具体来说是动态代理模式,动态代理模式一般实现AOP框架,不懂AOP的童鞋看这里.我前面的博文 ...

  7. (原创)c++11改进我们的模式之改进命令模式

    模式虽然精妙,却难完美,比如观察者模式中观察者生命周期的问题:比如访问者模式中循环依赖的问题等等:其它很多模式也存在这样那样的一些不足之处,如使用场景受限.实现复杂.不够简洁.不够通用等.但我觉得不足 ...

  8. (原创)c++11改进我们的模式之改进访问者模式

    本次讲c++11改进我们的模式之改进访问者模式 访问者模式是GOF23个设计模式中比较复杂的模式之一,但是它的功能也很强大,非常适合稳定的继承层次中对象的访问,可以在不修改被访问对象的情况下,动态添加 ...

  9. (原创)C++11改进我们的程序之简化我们的程序(七)

    这次要讲的内容是:c++11中的tuple(元组).tuple看似简单,其实它是简约而不简单,可以说它是c++11中一个既简单又复杂的东东,关于它简单的一面是它很容易使用,复杂的一面是它内部隐藏了太多 ...

随机推荐

  1. Android开发环境——调试器 DDMS相关内容汇总

       Android开发环境将分为SDK相关内容.Eclipse ADT相关内容.模拟器AVD相关内容.调试器DDMS相关内容.日志LogCat相关内容.连接驱动ADB相关内容.内存泄露检测工具MAT ...

  2. ios实例开发精品文章推荐(7.22)

    UIView 基本方法 UIView的一些基本方法理解:loadView.viewDidLoad.viewDidUnload.viewWillAppear,viewWillDisappear init ...

  3. 开始 App前 需要考虑的几项

    来源:Limboy's HQ http://t.cn/R5sEDMJ 随着工具链的完善,语言的升级以及各种优质教程的涌现,做一个 App 的成本也越来越低了.尽管如此,有些事情最好前期就做起来,避免当 ...

  4. Using 1-Wire device with Intel Galileo

    Using 1-Wire device with Intel Galileo 3 Replies Many people have had trouble with getting 1-Wire de ...

  5. Ubuntu菜鸟入门(十六)—— 安装视频播放器vlc

    sudo add-apt-repository ppa:videolan/master-daily sudo apt-get update sudo apt-get install vlc Ubunt ...

  6. android 安卓APP获取手机设备信息和手机号码的代码示例

    下面我从安卓开发的角度,简单写一下如何获取手机设备信息和手机号码 准备条件:一部安卓手机.手机SIM卡确保插入手机里.eclipse ADT和android-sdk开发环境 第一步:新建一个andro ...

  7. C# winform DataGridView 常见属性

    C# winform DataGridView 属性说明① 取得或者修改当前单元格的内容 ② 设定单元格只读 ③ 不显示最下面的新行 ④ 判断新增行 ⑤ 行的用户删除操作的自定义 ⑥ 行.列的隐藏和删 ...

  8. GIF Brewery for Mac(录制 Gif 动图工具)安装

    1.软件简介    GIF Brewery 一款用于录制 Gif 动图等的工具. 2.资源列表 链接 提取密码 系统要求 软件语言 GIF Brewery for Mac v3.9.5 ltmf ma ...

  9. [转]同步对象Event的用法

    同步对象Event的用法  首先介绍CreateEvent是创建windows事件的意思,作用主要用在判断线程退出,线程锁定方面.  CreateEvent函数功能描述:创建或打开一个命名的或无名的事 ...

  10. postman 定义并使用全局变量

    第一步:找到并打开右上角的设置图案  第二步,点击“Global” 按钮 第三步.如图所示,定义全局变量,然后点击“save”即可 第四步:如何使用全局变量,只需要 {{ key }} 即可,如图所示 ...