前言

我们都知道函数的基本形式为: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. 01-19asp.net网站--关于“应用程序中的服务器错误(需添加"Jquery"ScriptRescourseMapping)”

    一般打开网页进行加载时(有缓存),会弹出以下对话框. 但是如果网页加载后出现以下错误,就是应用程序的问题了.如果出现这种问题,就需要在安装Csharp的根目录下,找到一个名为.dll结尾的Jquery ...

  2. day17 11.JdbcUtils工具抽取

    连接数据库的四个必要条件:driverclass.url.username.password. package cn.itcast.utils; import java.sql.Connection; ...

  3. Ajax02 什么是json、json语法、json的使用、利用jQuery实现ajax

    目录 1什么是json 2json语法 3json的使用 4利用jQuery实现ajax编程 1 什么是json JavaScript Object Notation(JavaScript 对象表示法 ...

  4. ReentrantLock的简单使用

    ReentrantLock: /** * ReentrantLock测试逻辑类 */ public class MyService { private Lock lock = new Reentran ...

  5. java中字符串处理、串联和转换的几个常用方法,以及如果需要自己编程实现的具体实施步骤。

    What? 如何分类? 如何使用? //String 类提供处理 Unicode 代码点(即字符)(TT观点:指的莫非就是对象的意思.)和 Unicode 代码单元(即 char 值)的方法.(TT观 ...

  6. loj10100 网络

    这个题目描述好难理解呀qwq... 传送门 分析 在读懂题之后我们不难发现这道题实际就是在求一张图中有多少个割点.只需要注意读入方式即可. 代码 #include<iostream> #i ...

  7. python3-while与continue

    # Auther: Aaron Fan #要返回到循环开头,并根据条件测试结果决定是否继续执行循环,可使用continue#执行continue语句,让python忽略余下的代码 #只打印1到10之间 ...

  8. SDUT 3402 数据结构实验之排序五:归并求逆序数

    数据结构实验之排序五:归并求逆序数 Time Limit: 40MS Memory Limit: 65536KB Submit Statistic Problem Description 对于数列a1 ...

  9. struts学习记录

    see also:http://blog.csdn.net/chenggil10/article/details/5965806#_Toc250472631 0.struts2每一个请求,都new一个 ...

  10. java全栈day33--html

    本天要完成6个任务,并且布局静态页面(首页)详细分为六个部分  如下 网站信息页面案例(字体标签.排版标签) 网站图片信息页面案例(图片标签) 网站友情链接页面案例(列表标签) 网站首页案例(表格标签 ...