从C ++函数返回多个值
有一个首选的方法来从C + +函数返回多个值? 例如,想象一个将两个整数相除的函数,并返回商和余数。 我通常看到的一种方法是使用参考参数:
void divide(int dividend, int divisor, int& quotient, int& remainder);
一个变体是返回一个值,并通过参考parameter passing另一个值:
int divide(int dividend, int divisor, int& remainder);
另一种方法是声明一个结构来包含所有的结果并返回:
struct divide_result { int quotient; int remainder; }; divide_result divide(int dividend, int divisor);
其中一种方法通常是优选的还是还有其他build议?
编辑:在现实世界的代码中,可能有两个以上的结果。 它们也可以是不同的types。
为了返回两个值,我使用了一个std::pair
(通常是typedef'd)。 你应该看看boost::tuple
(在C ++ 11和更新的版本里有std::tuple
)两个以上的返回结果。
就个人而言,我通常不喜欢返回参数的原因有很多:
- 调用哪些参数在哪里,哪些是不明显的
- 您通常必须创build一个局部variables来捕获结果,而返回值可以内联使用(这可能是也可能不是一个好主意,但至less您可以select)
- 对我来说,在一个function上有一个“入门”和一个“出门”似乎更清洁 – 所有的input都在这里,所有的输出都出现在那里
- 我喜欢尽可能缩短参数列表
对于pair / tuple技术我也有一些保留。 主要是往往没有自然顺序的返回值。 代码的读者如何知道result.first是商还是余数? 而实施者可以改变顺序,这将破坏现有的代码。 如果这些值是相同的types,那么这是特别危险的,所以不会产生编译器错误或警告。 实际上,这些参数也适用于返回参数。
这里是另一个代码示例,这个稍微不重要:
pair<double,double> calculateResultingVelocity(double windSpeed, double windAzimuth, double planeAirspeed, double planeCourse); pair<double,double> result = calculateResultingVelocity(25, 320, 280, 90); cout << result.first << endl; cout << result.second << endl;
这是否打印地面速度和路线,或路线和地面速度? 这并不明显。
比较这个:
struct Velocity { double speed; double azimuth; }; Velocity calculateResultingVelocity(double windSpeed, double windAzimuth, double planeAirspeed, double planeCourse); Velocity result = calculateResultingVelocity(25, 320, 280, 90); cout << result.speed << endl; cout << result.azimuth << endl;
我认为这更清楚。
所以我认为我的第一select一般是结构技术。 在某些情况下,这个元组/想法可能是一个很好的解决scheme。 我想尽可能避免返回参数。
在C ++ 11中,您可以:
#include <tuple> std::tuple<int, int> divide(int dividend, int divisor) { return std::make_tuple(dividend / divisor, dividend % divisor); } #include <iostream> int main() { using namespace std; int quotient, remainder; tie(quotient, remainder) = divide(14, 3); cout << quotient << ',' << remainder << endl; }
在C ++ 17(现在称为C ++ 1z)中:
#include <tuple> std::tuple<int, int> divide(int dividend, int divisor) { return {dividend / divisor, dividend % divisor}; } #include <iostream> int main() { using namespace std; auto [quotient, remainder] = divide(14, 3); cout << quotient << ',' << remainder << endl; }
或与结构:
#include <tuple> auto divide(int dividend, int divisor) { struct result {int quotient; int remainder;}; return result {dividend / divisor, dividend % divisor}; } #include <iostream> int main() { using namespace std; auto result = divide(14, 3); cout << result.quotient << ',' << result.remainder << endl; // or auto [quotient, remainder] = divide(14, 3); cout << quotient << ',' << remainder << endl; }
std::pair<int, int> divide(int dividend, int divisor) { // : return std::make_pair(quotient, remainder); } std::pair<int, int> answer = divide(5,2); // answer.first == quotient // answer.second == remainder
std :: pair本质上就是你的结构解决scheme,但是已经为你定义了,并且准备适应任何两种数据types。
它完全取决于实际function和多重值的含义,以及它们的大小:
- 如果它们与你的分数例子中的相关,那么我会去结构或类的实例。
- 如果它们不是真正相关的,不能被分组到一个类/结构中,那么也许你应该把你的方法重构成两个。
- 根据您要返回的值的内存大小,您可能希望返回指向类实例或结构体的指针,或者使用引用参数。
面向对象的解决scheme是创build一个比例类。 它不需要任何额外的代码(会节省一些),会更清楚/更清晰,并会给你一些额外的重构,让你清理这个类以外的代码。
其实我觉得有人build议返回一个结构,这个结构已经足够接近了,但是隐藏了这个需要用构造函数和几个方法来完全思考的类的意图,事实上,你最初提到的“方法”对)应该很可能是这个类的成员返回自己的一个实例。
我知道你的例子只是一个“例子”,但事实是,除非你的函数做得比任何函数都要做得更好,否则如果你想让它返回多个值,你肯定会丢失一个对象。
不要害怕创造这些小课堂去做一些小小的工作 – 这就是面向对象的魔法 – 你最终会把它分解,直到每一种方法都非常小而简单,每一个小课堂都可以理解。
另一件事情应该是一个错误的指标:在OO中你基本上没有数据 – OO不是关于传递数据,一个类需要在内部pipe理和操作它自己的数据,任何数据传递(包括访问器)是一个迹象,你可能需要重新思考一些事情..
在C(以及C ++)标准中用<stdlib.h>
(或<cstdlib>
)中的div
, ldiv
(和C99, lldiv
)函数返回结构的先例。
“返回值和返回参数的混合”通常是最不干净的。
有一个函数返回一个状态并通过返回参数返回数据在C中是明智的; 在C ++中不太明显,您可以使用exception来中继失败信息。
如果有两个以上的返回值,那么类似结构的机制可能是最好的。
使用C ++ 17,您还可以返回一个或多个不可移动/不可复制的值 (在某些情况下)。 通过新的保证返回值优化返回不可移动的types的可能性,它与聚合 ,以及可以被称为模板构造函数很好地组成。
template<typename T1,typename T2,typename T3> struct many { T1 a; T2 b; T3 c; }; // guide: template<class T1, class T2, class T3> many(T1, T2, T3) -> many<T1, T2, T3>; auto f(){ return many{string(),5.7, unmovable()}; }; int main(){ // in place construct x,y,z with a string, 5.7 and unmovable. auto [x,y,z] = f(); }
关于这个的漂亮的事情是,它是保证不会造成任何复制或移动。 你也可以做many
结构variables的例子。 更多细节:
返回C ++ 17可变参数模板“构造演绎指南”的可变集合(结构)和语法
如果你的函数通过引用返回一个值,编译器在调用其他函数时就不能将它存储在寄存器中,因为从理论上说,第一个函数可以将传递给它的variables的地址保存在一个全局可访问的variables中,改变它,所以编译器将有(1)在调用其他函数之前将寄存器中的值保存到内存中,(2)在任何这样的调用之后当需要时从内存中再次读取它。
如果您通过参考返回,您的程序的优化将受到影响
使用结构或类作为返回值。 使用std::pair
现在可以工作,但是
- 如果您稍后决定要更多的信息返回,它是不灵活的;
- 从头文件中的函数声明,返回的内容和顺序是不是很清楚。
使用自我logging成员variables名称返回一个结构对于任何使用你的函数的人来说都可能不太容易出错。 把我的同事的帽子放一会儿,您的divide_result
结构对于我来说是一个潜在的用户,您的function很容易在2秒后立即明白。 ouputinput参数或神秘的对和元组需要更多的时间来通读,并可能被错误地使用。 而且很有可能甚至在使用函数几次之后,我仍然不会记住参数的正确顺序。
我倾向于在这样的函数中使用out-vals,因为我坚持函数返回成功/错误代码的范例,我喜欢保持一致。
在这里,我正在写一个程序,在c ++中返回多个值(超过两个值)。 这个程序可以在C ++ 14(G ++ 4.9.2)中执行。 程序就像一个计算器。
# include <tuple> # include <iostream> using namespace std; tuple < int,int,int,int,int > cal(int n1, int n2) { return make_tuple(n1/n2,n1%n2,n1+n2,n1-n2,n1*n2); } int main() { int qut,rer,add,sub,mul,a,b; cin>>a>>b; tie(qut,rer,add,sub,mul)=cal(a,b); cout << "quotient= "<<qut<<endl; cout << "remainder= "<<rer<<endl; cout << "addition= "<<add<<endl; cout << "subtraction= "<<sub<<endl; cout << "multiplication= "<<mul<<endl; return 0; }
所以,你可以清楚地知道,用这种方法你可以从一个函数返回多个值。 使用std :: pair只能返回2个值,而std :: tuple可以返回两个以上的值。
其他select包括数组, 发生器和控制反转 ,但在这里没有一个是合适的。
有些人(例如,历史上的Win32中的微软)倾向于使用参考参数来简化操作,因为很明显,谁分配以及它将如何在堆栈上显示,减less结构的扩散,并允许单独的返回值成功。
“纯粹”的程序员更喜欢这个结构,假设它是函数值(这里就是这种情况),而不是函数偶然碰到的东西。 如果你有一个更复杂的过程,或者有状态的东西,你可能会使用引用(假设你有一个不使用类的理由)。
我会说没有首选的方法,这一切都取决于你要做什么的回应。 如果结果将在进一步处理中一起使用,那么结构是有意义的,如果不是的话,我倾向于将其作为单独的引用传递,除非函数将被用于复合语句中:
x = divide( x, y, z ) + divide( a, b, c );
我经常select在参数列表中通过引用传递“out structure”,而不是通过返回新结构的副本开销(但这是小事)。
void divide(int dividend, int divisor, Answer &ans)
参数混淆了吗? 作为参考发送的参数表明该值将改变(而不是const引用)。 明智的命名也消除了混淆。
为什么你坚持一个具有多个返回值的函数? 使用OOP,你可以使用一个提供一个常规函数的类,它有一个单一的返回值,以及任何数量的附加“返回值”,如下所示。 优点是调用者可以select查看额外的数据成员,但不需要这样做。 这是复杂的数据库或networking调用的首选方法,在发生错误时可能需要大量额外的返回信息。
为了回答你原来的问题,这个例子有一个方法来返回大部分调用者可能需要的商,另外,在方法调用之后,你可以将余数作为数据成员。
class div{ public: int remainder; int quotient(int dividend, int divisor){ remainder = ...; return ...; } };
提升元组将是我的一个从函数返回多个值的广义系统的首选。
可能的例子:
include "boost/tuple/tuple.hpp" tuple <int,int> divide( int dividend,int divisor ) { return make_tuple(dividend / divisor,dividend % divisor ) }
我们可以声明这样的函数,它返回一个结构types用户定义的variables或一个指针。 而通过结构的性质,我们知道C中的一个结构可以保存多个不对称types的值(即一个intvariables,四个charvariables,两个浮点variables等等)