当前位置:  开发笔记 > 编程语言 > 正文

从C++函数返回多个值

如何解决《从C++函数返回多个值》经验,为你挑选了9个好方法。

有没有从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);

这些方式中的一种通常是首选,还是有其他建议?

编辑:在现实世界的代码中,可能会有两个以上的结果.它们也可以是不同类型的.



1> Rob..:

为了返回两个值,我使用a std::pair(通常是typedef'd).您应该查看boost::tuple(在C++ 11和更新版本中,有std::tuple两个以上的返回结果).

随着C++ 17中结构化绑定的引入,返回std::tuple应该成为公认的标准.


在C++ 11中,您可以使用`std :: tuple`.
如果你想接受一个函数的多个值,一个方便的方法是使用`std :: tie` http://stackoverflow.com/a/2573822/502144
如果您要使用元组,为什么不将它们用于成对.为什么有特例?
+1元组.请记住在结构中返回的大对象与通过引用传递的性能后果.
Fred,是的boost :: tuple可以做到:)
@MooingDuck他们在C++ 17中更加出色:`auto&[a,b] = func();`
Ferruccio:Boost并不是一个轻量级的依赖.如果你的项目还没有依赖它,多次返回值的`boost :: tuple`对于拖动Boost来说并不是一个好例子.(这来自于花了一个多小时在Windows上摆弄Boost特性的人和AIX;看起来像Linux上的"永远存在"依赖关系是其他地方严重头痛的根源.使用它,但要注意它.)

2> pepper_chico..:

在C++ 11中,您可以:

#include 

std::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中:

#include 

std::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};
}

#include 

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;
}


@UchiaItachi同样关注函数参数,您可以给它们命名,但语言甚至不强制执行,并且参数名称在读取时在调用站点没有值.另外,在一次返回时,你只有一个类型,但是名字也可能有用,元组你只需要加倍问题,所以imo,语言只是缺乏以多种方式自我记录,而不仅仅是这个.
我对返回元组的函数有一个顾虑.假设上面的函数原型在标题中,那么如何在不理解函数定义的情况下知道第一个和第二个返回值是什么意思?商 - 余数或余数 - 商.
@pepper_chico如果想把`divide`的函数定义放到一个单独的cpp文件中怎么办?我得到错误`错误:在扣除'auto'之前使用'auto divide(int,int)'.我该如何解决这个问题?

3> Fred Larson..:

就个人而言,我通常不喜欢返回参数,原因如下:

在调用中并不总是很明显哪些参数是ins而哪些是out

你通常必须创建一个局部变量来捕获结果,而返回值可以内联使用(这可能是也可能不是一个好主意,但至少你有选择)

对我来说似乎更清洁一个功能的"门"和"外门" - 所有输入都在这里,所有输出都出现在那里

我想让我的论点清单尽可能短

我对这对/元组技术也有一些保留意见.主要是,返回值通常没有自然顺序.代码的读者如何知道result.first是商还是余数?并且实现者可以更改顺序,这将破坏现有代码.如果值是相同的类型,则这尤其隐蔽,因此不会生成编译器错误或警告.实际上,这些参数也适用于返回参数.

这是另一个代码示例,这个有点不那么简单:

pair calculateResultingVelocity(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;

我认为这更清楚了.

所以我认为我的第一选择是结构技术.在某些情况下,对/元组的想法可能是一个很好的解决方案.我想尽可能避免返回参数.


+1.一个函数应该返回*one*"thing",而不是一个以某种方式排序的"tuple of things".
大约15年前,当我们发现提升时,我们使用了元组,因为它非常方便.加班我们在可读性方面遇到了不利,特别是对于具有相同类型的元组(例如元组;哪一个是哪个).所以最近我们更习惯于引入一个小的POD结构,其中至少成员变量的名称表明一些合理的东西.
@anton_rh:C++ 14允许使用`auto`返回本地类型,因此`auto result = my_func();`很容易获得.

4> James Curran..:
std::pair divide(int dividend, int divisor)
{
   // :
   return std::make_pair(quotient, remainder);
}

std::pair answer = divide(5,2);
 // answer.first == quotient
 // answer.second == remainder

std :: pair本质上是你的结构解决方案,但已经为你定义,并准备适应任何两种数据类型.


也不是自我记录.你还记得哪个x86寄存器是DIV的剩余部分吗?
这对我的简单例子有用.但是,一般情况下,返回的值可能超过两个.

5> Stewart John..:

它完全取决于实际功能和多个值的含义及其大小:

如果它们与您的分数示例中的相关,那么我将使用结构或类实例.

如果它们不是真正相关的并且不能被分组到类/结构中,那么也许你应该将你的方法重构为两个.

根据要返回的值的内存大小,您可能希望返回指向类实例或结构的指针,或使用引用参数.



6> Bill K..:

OO解决方案是创建比率等级.它不需要任何额外的代码(会节省一些),会更清晰/更清晰,并会给你一些额外的重构,让你清理这个类之外的代码.

实际上我认为有人建议返回一个结构,这个结构足够接近,但隐藏了这个需要是一个完全深思熟虑的类的意图,其中包含构造函数和一些方法,实际上,你最初提到的"方法"(作为返回pair)应该很可能是这个类的成员返回自己的实例.

我知道你的例子只是一个"例子",但事实是除非你的函数比任何函数都要做的更多,如果你想要它返回多个值,你几乎肯定会丢失一个对象.

不要害怕创建这些小课程来完成一些小工作 - 这就是OO的魔力 - 你最终会将其分解,直到每个方法都非常小而且简单,每个类都小而易懂.

另一件事应该是一个错误的指示:在OO中你基本上没有数据 - OO不是关于传递数据,类需要在内部管理和操纵它自己的数据,任何数据传递(包括访问器)是一个迹象,你可能需要重新思考一些东西..



7> Jonathan Lef..:

已有先例返回结构在C(因此C++)标准与div,ldiv(并且,在C99,lldiv从)函数(或).

"返回值和返回参数的混合"通常是最不干净的.

让函数返回状态并通过返回参数返回数据在C中是明智的; 在C++中,您可以使用异常来转发故障信息.

如果有两个以上的返回值,那么类似结构的机制可能是最好的.



8> Johan Lundbe..:

使用C++ 17,您还可以返回一个或多个不可移动/不可复制的值(在某些情况下).返回不可移动类型的可能性来自新的保证返回值优化,并且它与聚合以及可以称为模板化构造函数的组合很好 .

template
struct 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();
}

关于这一点的好处是保证不会导致任何复制或移动.您也可以使示例manystruct variadic.更多细节:

返回C++ 17可变参数模板'构造演绎指南'的可变参数聚合(结构)和语法



9> Michel..:

使用结构或类作为返回值.使用std::pair可能现在可以工作,但是

    如果您稍后决定要返回更多信息,那么它是不灵活的;

    从标题中的函数声明中返回的内容以及以什么顺序返回的内容并不十分清楚.

对于使用您的函数的任何人来说,返回具有自记录成员变量名称的结构可能不会更容易出错.把我的同事戴上帽子,你的divide_result结构很容易让我,你的功能的潜在用户,2秒后立即理解.使用输出参数或神秘对和元组进行混乱会花费更多时间来阅读并且可能使用不当.甚至在使用该函数几次后,我仍然不记得参数的正确顺序.

推荐阅读
Life一切安好
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有