有没有从C++函数返回多个值的首选方法?例如,假设一个函数分割两个整数并返回商和余数.我经常看到的一种方法是使用参考参数:
void divide(int dividend, int divisor, int& quotient, int& remainder);
一种变化是返回一个值并通过引用参数传递另一个值:
int divide(int dividend, int divisor, int& remainder);
另一种方法是声明一个包含所有结果的结构并返回:
struct divide_result { int quotient; int remainder; }; divide_result divide(int dividend, int divisor);
这些方式中的一种通常是首选,还是有其他建议?
编辑:在现实世界的代码中,可能会有两个以上的结果.它们也可以是不同类型的.
为了返回两个值,我使用a std::pair
(通常是typedef'd).您应该查看boost::tuple
(在C++ 11和更新版本中,有std::tuple
两个以上的返回结果).
随着C++ 17中结构化绑定的引入,返回std::tuple
应该成为公认的标准.
在C++ 11中,您可以:
#includestd::tuple divide(int dividend, int divisor) { return std::make_tuple(dividend / divisor, dividend % divisor); } #include int main() { using namespace std; int quotient, remainder; tie(quotient, remainder) = divide(14, 3); cout << quotient << ',' << remainder << endl; }
在C++ 17中:
#includestd::tuple divide(int dividend, int divisor) { return {dividend / divisor, dividend % divisor}; } #include int main() { using namespace std; auto [quotient, remainder] = divide(14, 3); cout << quotient << ',' << remainder << endl; }
或结构:
auto divide(int dividend, int divisor) { struct result {int quotient; int remainder;}; return result {dividend / divisor, dividend % divisor}; } #includeint 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; }
就个人而言,我通常不喜欢返回参数,原因如下:
在调用中并不总是很明显哪些参数是ins而哪些是out
你通常必须创建一个局部变量来捕获结果,而返回值可以内联使用(这可能是也可能不是一个好主意,但至少你有选择)
对我来说似乎更清洁一个功能的"门"和"外门" - 所有输入都在这里,所有输出都出现在那里
我想让我的论点清单尽可能短
我对这对/元组技术也有一些保留意见.主要是,返回值通常没有自然顺序.代码的读者如何知道result.first是商还是余数?并且实现者可以更改顺序,这将破坏现有代码.如果值是相同的类型,则这尤其隐蔽,因此不会生成编译器错误或警告.实际上,这些参数也适用于返回参数.
这是另一个代码示例,这个有点不那么简单:
paircalculateResultingVelocity(double windSpeed, double windAzimuth, double planeAirspeed, double planeCourse); pair 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;
我认为这更清楚了.
所以我认为我的第一选择是结构技术.在某些情况下,对/元组的想法可能是一个很好的解决方案.我想尽可能避免返回参数.
std::pairdivide(int dividend, int divisor) { // : return std::make_pair(quotient, remainder); } std::pair answer = divide(5,2); // answer.first == quotient // answer.second == remainder
std :: pair本质上是你的结构解决方案,但已经为你定义,并准备适应任何两种数据类型.
它完全取决于实际功能和多个值的含义及其大小:
如果它们与您的分数示例中的相关,那么我将使用结构或类实例.
如果它们不是真正相关的并且不能被分组到类/结构中,那么也许你应该将你的方法重构为两个.
根据要返回的值的内存大小,您可能希望返回指向类实例或结构的指针,或使用引用参数.
OO解决方案是创建比率等级.它不需要任何额外的代码(会节省一些),会更清晰/更清晰,并会给你一些额外的重构,让你清理这个类之外的代码.
实际上我认为有人建议返回一个结构,这个结构足够接近,但隐藏了这个需要是一个完全深思熟虑的类的意图,其中包含构造函数和一些方法,实际上,你最初提到的"方法"(作为返回pair)应该很可能是这个类的成员返回自己的实例.
我知道你的例子只是一个"例子",但事实是除非你的函数比任何函数都要做的更多,如果你想要它返回多个值,你几乎肯定会丢失一个对象.
不要害怕创建这些小课程来完成一些小工作 - 这就是OO的魔力 - 你最终会将其分解,直到每个方法都非常小而且简单,每个类都小而易懂.
另一件事应该是一个错误的指示:在OO中你基本上没有数据 - OO不是关于传递数据,类需要在内部管理和操纵它自己的数据,任何数据传递(包括访问器)是一个迹象,你可能需要重新思考一些东西..
已有先例返回结构在C(因此C++)标准与div
,ldiv
(并且,在C99,lldiv
从)函数
(或
).
"返回值和返回参数的混合"通常是最不干净的.
让函数返回状态并通过返回参数返回数据在C中是明智的; 在C++中,您可以使用异常来转发故障信息.
如果有两个以上的返回值,那么类似结构的机制可能是最好的.
使用C++ 17,您还可以返回一个或多个不可移动/不可复制的值(在某些情况下).返回不可移动类型的可能性来自新的保证返回值优化,并且它与聚合以及可以称为模板化构造函数的组合很好 .
templatestruct many { T1 a; T2 b; T3 c; }; // guide: template many(T1, T2, T3) -> many ; 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
struct variadic.更多细节:
返回C++ 17可变参数模板'构造演绎指南'的可变参数聚合(结构)和语法
使用结构或类作为返回值.使用std::pair
可能现在可以工作,但是
如果您稍后决定要返回更多信息,那么它是不灵活的;
从标题中的函数声明中返回的内容以及以什么顺序返回的内容并不十分清楚.
对于使用您的函数的任何人来说,返回具有自记录成员变量名称的结构可能不会更容易出错.把我的同事戴上帽子,你的divide_result
结构很容易让我,你的功能的潜在用户,2秒后立即理解.使用输出参数或神秘对和元组进行混乱会花费更多时间来阅读并且可能使用不当.甚至在使用该函数几次后,我仍然不记得参数的正确顺序.