前言

我们都知道函数的基本形式为: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. 1106SQLserver基础--变量、运算符的使用,if...else,while语句

    数据库---变量(对数据库中的数据没有任何影响) 作用:临时存储数据的作用,起一个衔接的作用,为了方便理解存储过程. 例:Declare @hello varchar(20) Set @hello=’ ...

  2. 问题:C# ToString("P");结果:c#中的常用ToString()方法总结

    c#中的常用ToString()方法总结   很多类都重写了ToString方法, 导致很多类的tostring到底执行了什么,有哪些参数,都不清楚 对于int,double等的tostring: C ...

  3. Acviticy.this 和 getApplicationContext()的区别

    用AlertDialog 举例 AlertDialog对象是依赖于一个View的,而View是和一个Activity对应的,在Activity销毁的时候它也就销毁了,不会再存在.Activity.th ...

  4. 前端设置cookie,以及jQuerycookie的使用

  5. Codeforces 1114D Flood Fill (区间DP or 最长公共子序列)

    题意:给你n个颜色块,颜色相同并且相邻的颜色块是互相连通的(连通块).你可以改变其中的某个颜色块的颜色,不过每次改变会把它所在的连通块的颜色也改变,问最少需要多少次操作,使得n个颜色块的颜色相同. 例 ...

  6. Ros学习——roslaunch

    roslaunch:启动定义在launch文件中的多个节点 1.launch文件解析 <launch> #以launch标签开头以表明这是一个launch文件 #两个节点分组并以'命名空间 ...

  7. 形式化验证工具(PAT)2PC协议学习

    今天我们来看看2PC协议,不知道大家对2PC协议是不是了解,我们先简单介绍一下. 两阶段提交协议(two phase commit protocol, 2PC)可以保证数据的强一致性,许多分布式关系型 ...

  8. [学习笔记]Vfork深入理解

    #include<stdio.h> #include<stdlib.h> #include<string.h> #include <unistd.h> ...

  9. android studio中退出时弹出对话框

    在app中总是不小心点击了退出怎么办?当然是加个弹出的提示框了,本人新手,就加在主界面上了 @Override public boolean onKeyDown(int keyCode, KeyEve ...

  10. chrome安装postman插件

    参考http://www.cnplugins.com/zhuanti/how-to-make-crx-install.html 下载地址:http://www.cnplugins.com/down/p ...