页面

分类

关于函数返回值

2016/6/5, by wingfire ; 分类: 计算机技术, 程序设计; 2 comments

你青睐什么样的编程观点?实效的或是符合理论的?命令式的还是函数式的?无论是哪一种,函数的返回值在其中都起着极其重要的作用。然而,受某些风格,甚至仅仅是某些库的影响,存在着一种抛弃函数返回值的风气。即使在某些情况下没有彻底抛弃,也被降格为二等公民使用,不得不让人大跌眼镜。对C/C++这类语言来说,参数很容易有多个,但是返回值只有一个,这意味着返回值的设计比参数的设计更重要。不可思议的是,这么重要的设施,没有成为设计上的兵家必争之地,反而就这么被许多人固化为传输错误码的通道,甚至干脆被抛弃了。买椟还珠,也不过就是这样吧?

确实,在某些库(如COM相关的)中普遍存在着用返回值来标示错误码的做法,但是这么做不意味着都是正确的,更不意味着这么做都是好的。其中一些是因为语言本身的缺陷所导致的困扰,另一些是工程上的优化和折衷,还有一些则完全是错误传统的继承和扩散。在语法上,C和C++都只有一个返回值。然而存在迂回的方法模仿多个返回值。例如,为返回值定义一个struct,将所有的函数输出定义在这个struct中。这么做是可行的,但是一来太麻烦,二来受制于传统,并未被广泛采用。C++11中提供了更好的工具,std::tuple。然而,对tuple的使用仍然不够广泛,传统的库也没有完成这样的转变,有些库因为接口依赖或兼容方面的原因,也不适合依赖标准库。即便如此,我们仍然应该认识到函数返回值被弱化是一种工程限制或错误实践,而绝不是学习和效仿的对象。在许多现在语言中,多返回值都是受到语言直接支持的。

以C运行库和Posix库的函数为例,大多数情况下函数的返回值都是和函数职责直接相关的,如open,并非仅仅返回错误码。用errno存储错误码当然不理想,也带来很多问题,但比起放弃返回值还是好很多。放弃返回值,函数的输出只好通过输出参数来传递。输出参数带来的不仅仅是函数调用形式的变化,也丧失了重要的逻辑表达方式。程序逻辑很重要,逻辑的表达方式同样重要。不同于返回值方式,输出参数方式除了要用额外的语句定义输出变量外(导致表达啰嗦),还妨碍组合调用且丧失了链式处理。if(obj.foo().bar())这样极具表达力的语句是无法在没有返回值的情况下完成的。g(foo(), bar())这样简单的表达也会啰嗦到让人发指:

      T f;
      U b;
      foo(&f);
      bar(&b);
      R result;
      g(&result, f, b);

这还是极简单的例子,就不要提稍微复杂点的了。

也不要辩解说什么有迂回的方式能达成链式调用和组合调用,然而正如C/C++有迂回的方式支持多返回值,然而那并没有在实践中正常发挥作用。

说的远一点,在C++ Java这样的语言中引入异常机制付出了多大的代价?很大!无论是教育,实践,甚至是运行时代码,都付出了不小的代价。可是我们为什么要花那么大代价引入异常?仅仅是为了“优雅地“传播错误码吗?仅仅就传播错误码而言,异常算不上多优雅。但是,异常解放了返回值,仅此一点,就值回我们为异常付出的所有努力。

但还有很多人,继续走在放弃返回值的道路上。可悲。

评论

steedhorse, at 2016/8/19 上午10:24

为你返回一个数组,里面全是“赞”~~

wingfire, at 2016/8/19 上午11:15

呀!你都寻到这里来"赞"我啦 o^_o

添加评论:

 
 the email would not displayed
 

您可以使用 Markdown 语法。

您必须启用浏览器的 JavaScript 功能才能发表评论。