前言

我们都知道函数的基本形式为:output f(input),且先按这种形式进行input与output的分析,我们的input与output可以有更好的设计方式,而我们的output是选择使用return by value还是return by reference也会有一定的思考,使得我们的函数更加"函数化"。

input

函数的input一般指通过传参给函数,这些参数一般是通过传递引用到const参数(const T&)来表示(当然基本类型直接传value也是ok的)。我们在函数里可以调用这个参数,但同时因为这个参数是const类型,所以不能进行修改,所以说const T&的input在函数中的作用只是一个“只读”的参数。

如果我们想让传入的参数同时还有output的能力,这样表达可能有些奇怪,换一句话说就是我们希望我们传入的同时具有被修改的能力,这样在该函数中修改后我们甚至都不用返回这个input的新值,就能在其他函数中调用该参数的最新的值,这里只需要使用T &就行了。

output

再回到我们的函数基本形式上来:Output f(const Input& input),Output是函数的返回类型;但正如我们前面所说的,如果我们想让参数同时具有output的能力,或许我们可以这样写:

void f(const Input& input, Output& output);

这样,我们的output参数在函数中被修改,自然也就不用再返回Output了,这样不是非常的好么?

先说一下这个新方法(return by reference)的一些弊端:

1.不太符合语言的自然性

输出参数之所以是output,就因为它是输出(output类型)的呀.......假如我们就按刚才提到的方法做,我们看看我们在其他函数中应该怎么写:

Output output;
.......
f(input,output);

如果我们再加上几个output:

Output output1,output2,output3,output4;

f(input,output1);
f(input,output2);
f(input,output3);
g(input,output4);

相比我们如果用Output f(const Input& input)来表达:

Output output = f(input);

return by reference显得不自然,如果出现在工程项目里,一句f(input,output)可能都会让你忘记这个函数是想干什么,这个output又是什么东西;所以第一点,这个语法不够自然。

2.我们并不能确保我们的函数是真的将output赋予新值了

我们通过return by value还是可以较为方便的通过检测机制看到这个值是否发生变化的,但是如果是return by reference,我们实际上并不能确切的得知output是否确实发生改变了(在函数中再加入一个检测机制并不是一个好方法.......)

虽然利用return来返回类型确实不是被很多人使用,但是除非是真的无法用return来返回output,我一般都会建议利用return来返回,下面说说使用Output f(const Input& input)的好处:

性能

一部分人可能会认为通过return返回,需要拷贝赋值等等会耗费空间的举措,但是C++中已经有了很多语言机制去消除returning by value的copy,首要提及的自然是RVO(Return Value Optimisation):在这里举个例子吧,比如下面这个程序:

T f()
{
....
return T(constructor arguments);
} T t=f();

理论上,会创建三个T对象,第一个是在T f()的return中的构造对象,第二个是拷贝了上一个对象的临时对象,第三个则是将临时对象copy给t。但是实际上,RVO让我们的编译器移除了第一个与第二个对象,允许我们直接使用传递到f中的构造函数的参数来初始化t。有兴趣的可以参考一下这篇[博客(https://www.cnblogs.com/xkfz007/archive/2012/07/21/2602110.html)]。当然我们也有std::move移动构造,比如我们返回一个STL的容器的时候并不是拷贝而是会移动它,由于优化,与拷贝所耗费时间都是差不多的。

差错处理

正如我们关于return by reference的第二个缺陷,我们在return by value会有很好的办法去处理他:

bool f(const Input& input, Output& output);
Output output;
bool success = f(input, output);
if (success)
{
// use output ...
}

通过返回类型我们可以很好的知道是否执行成功,在网络编程中,我i们的基本函数bind()、connect()、listen()、socket()等等都是通过这种返回形式来执行差错处理的。

而在C++17标准中有了一个std::optional的方法,如果函数执行成功我们将得到正确的output,如果没有我们将返回一个空的值(empty),更方便我们的差错处理。

返回多个类型

想一想如果我们利用return by reference返回多个类型,该怎么写?

Output1 output1;
Output2 output2;
void f(const Input& intput, Output1& output1, Output2& output2);

当然我们也可以定义一个结构体,将所有数据存进来再通过return by reference返回:

struct Outputs
{
Output1 output1;
Output2 output2;
}; Outputs f(const Input& input);

但是如果我们return by value,就可以通过tuple来实现:

std::tuple<Output1, Output2> f(const Input& input);

Output1 output1;
Output2 output2;
std::tie(output1, output2) = f(inputs);

而在C++标准中我们可以通过auto直接返回多个参数:

auto [output1, output2] = f(const Input& input);

支持了这种结构化的绑定,会更加方便。

结论

或许用"该选择return by value还是return by reference"可能更好一些。自己的结论就是不到无法使用return by value的时候,出于对代码的清晰性和表达性的考量,尽量别使用return by reference。

让函数的input、output更"函数化"的更多相关文章

  1. PHP输出缓冲控制- Output Control 函数应用详解

    说到输出缓冲,首先要说的是一个叫做缓冲器(buffer)的东西.举个简单的例子说明他的作用:我们在编辑一篇文档时,在我们没有保存之前,系统是不会向磁盘写入的,而是写到buffer中,当buffer写满 ...

  2. EPANET中读取INPUT文件的函数文件——INPUT1.C/INPUT2.C/INPUT3.C

    首先介绍下这3个文件的关系:可以说INPUT1.C的函数粒度最大,它的函数getdata()就完成了整个INPUT文件数据的读入,该函数又调用了INPUT2.C中的部分函数,INPUT2.C文件中的函 ...

  3. 深入理解javascript函数进阶系列第二篇——函数柯里化

    前面的话 函数柯里化currying的概念最早由俄国数学家Moses Schönfinkel发明,而后由著名的数理逻辑学家Haskell Curry将其丰富和发展,currying由此得名.本文将详细 ...

  4. 实验与作业(Python)-02 Python函数入门与温度转换程序(函数、input、eval、int、float、列表)

    截止日期 实验目标 学会定义函数,使用函数.学会导入在某个文件中定义的函数. input获得值,然后通过eval或者int.float将其转换为相应的类型. 学会使用列表:访问列表.append.遍历 ...

  5. 学好Spark/Kafka必须要掌握的Scala技术点(三)高阶函数、方法、柯里化、隐式转换

    5. 高阶函数 Scala中的高阶函数包含:作为值的函数.匿名函数.闭包.柯里化等,可以把函数作为参数传递给方法或函数. 5.1 作为值的函数 定义函数时格式: val 变量名 = (输入参数类型和个 ...

  6. input()和print()函数同时输入输出多个数据--python3

    使用input()和print()函数同时输入输出多个数据,需要空格分割输入信息 #!/usr/bin/python3#-*- conding:utf-8 -*- name, age, QQ = in ...

  7. 使用QFileInfo类获取文件信息(在NTFS文件系统上,出于性能考虑,文件的所有权和权限检查在默认情况下是被禁用的,通过qt_ntfs_permission_lookup开启和操作。absolutePath()必须查询文件系统。而path()函数,可以直接作用于文件名本身,所以,path() 函数的运行会更快)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/Amnes1a/article/details/65444966QFileInfo类为我们提供了系统无 ...

  8. PHP7函数大全(4553个函数)

    转载来自: http://www.infocool.net/kb/PHP/201607/168683.html a 函数 说明 abs 绝对值 acos 反余弦 acosh 反双曲余弦 addcsla ...

  9. 文成小盆友python-num3 集合,函数,-- 部分内置函数

    本接主要内容: set -- 集合数据类型 函数 自定义函数 部分内置函数 一.set 集合数据类型 set集合,是一个无序且不重复的元素集合 集合基本特性 无序 不重复 创建集合 #!/bin/en ...

随机推荐

  1. 用UltraISO把硬盘文件制作成ISO格式

    转自:https://wenku.baidu.com/view/0052c88dcc22bcd126ff0cbf.html 用UltraISO把硬盘文件制作成ISO格式方法: 制作硬盘ISO文件步骤一 ...

  2. vue 跨域访问http

    axios用法: npm install axios --save-dev 2.导入: import axios from 'axios'; 3.使用($(form)需要先按装jQuery) axio ...

  3. 【摘自张宴的"实战:Nginx"】使用nginx的fastcgi_cache缓存php输出的内容

    亲自测试发现,fastcgi_cache虽然可以缓存生成的php输出的文件,但是有个弊端,在缓存的失效时间之内,你继续访问这个地址,输出的内容没有发生变化,即使数据库新增了数据或者删除了数据,所以不适 ...

  4. 高性能MySQL笔记-第5章Indexing for High Performance-004怎样用索引才高效

    一.怎样用索引才高效 1.隔离索引列 MySQL generally can’t use indexes on columns unless the columns are isolated in t ...

  5. 1054D&EZOJ #93 Changing Array

    传送门 分析 我们可以对一个数列求前缀和,如果pre[l]=pre[r]则我们可以知道区间[l,r]一定不合法 于是我们就要让不合法的区间尽量少 我们知道对于一个数$x$,他只受x和$(2^k-1)$ ...

  6. Luogu 2375 [NOI2014]动物园

    字胡串什么的一直不太会,感觉这题…还蛮本质的 考虑暴力求解:num[i]相当于从一直跳nxt,如果nxt[j] * 2 <= i 那么就累加答案 其实这是一个树的结构,也就是说跳到一个结点满足条 ...

  7. Android开发环境搭建及HelloWorld

    在开始Android开发之旅启动之前,首先要搭建环境,然后创建一个简单的HelloWorld.本文的主题如下: 1.环境搭建 1.1.JDK安装 1.2.Eclipse安装 1.3.Android S ...

  8. preventDefault()对象

    preventDefault() 方法 Event 对象 定义和用法 取消事件的默认动作. 语法 event.preventDefault() 说明 该方法将通知 Web 浏览器不要执行与事件关联的默 ...

  9. Core中间件——访问记录

    引言 上半年使用的thinkjs开发的node项目有一个优点就是所有请求都会有日志记录在控制台输出,里面包含了请求地址以及耗时.我就希望在.net中也可以实现这样子的功能,正好想到了中间件,所以就用中 ...

  10. Info.plist文件配置及注意事项

    1.Info.plist文件配置 常见配置 2.注意事项 Info.plist文件移动路径修改编译报错:could not read data from '/Users/lelight/Desktop ...