这次要讲的是: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. windows下卸载mysql5.5,升级为mysql5.7.25

    0. 停止mysql的服务 1. 卸载mysql5.5 1.1 使用360或者控制面板卸载mysql,为求干净,我使用的360,结果也需要手动清理 1.2 卸载完成之后到mysql的安装目录删掉该目录 ...

  2. Kafka流处理平台

    1. Kafka简介 Kafka是最初由Linkedin公司开发,是一个分布式.支持分区的(partition).多副本的(replica),基于zookeeper协调的分布式消息系统,它的最大的特性 ...

  3. 得到类所在的jar包路径

    //理论上用class.getProtectionDomain().getCodeSource().getLocation().getFile();比较准.  //不过有两个需要注意的:  //1.返 ...

  4. spring+mybatis+atomikos 实现JTA事务

    1. 选择哪种transaction manager?      在单数据源情况下,JDBC,Hibernate,ibatis等自带的 transaction manager已能用于处理事务.     ...

  5. 遇到影响服务器性能的cpuspeed 服务

    最近碰到一个很蛋痛的问题,,我在公司的代码上实现了一个功能,然后基于这个测试,结果比对数据发现每天少三千多万条,, 然后我各种优化,各种零碎部功能阉割,,还是丢数据! 之后,监控运行网卡----wat ...

  6. Nginx防爬虫或限制浏览器访问

    假定一个场景:某个网站它可能不希望被网络爬虫抓取,例如测试环境不希望被抓取,以免对用户造成误导,那么需要在该网站中申明,本站不希望被抓取.有如下方法: 方法一:修改nginx.conf,禁止网络爬虫的 ...

  7. oracle收购Mysql后,Mysql的安装配置方法

    自从Oracle收购MySQL后,略微发生了一些小小的变化,原来mysql安装完成后默认是没有密码的,但是新版的mysql安装完成后oracle提供了一个free password放着/root/.m ...

  8. bootstrap fileinput 文件上传

    最近因为项目需要研究了下bootstrap fileinput的使用,来记录下这几天的使用心得吧. 前台html页面的代码 <form role="form" id=&quo ...

  9. C# Random 生成不重复随机数

    命名空间:System 表示伪随机数生成器,一种能够产生满足某些随机性统计要求的数字序列的设备. 伪随机数是以相同的概率从一组有限的数字中选取的.所选数字并不具有完全的随机性,因为它们是用一种确定的数 ...

  10. 代码管理(三)sourcetree 的使用

    一.SourceTree简介   SourceTree 是 Windows 和Mac OS X 下免费的 Git 和 Hg 客户端,拥有可视化界面,容易上手操作.同时它也是Mercurial和Subv ...