从C ++函数返回多个值

时间:2008-11-26 15:19:01

标签: c++

是否有一种从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);

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

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

22 个答案:

答案 0 :(得分:183)

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

随着C ++ 17中结构化绑定的引入,返回std::tuple应该成为可接受的标准。

答案 1 :(得分:139)

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

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

或结构:

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

答案 2 :(得分:117)

就个人而言,我通常不喜欢返回参数有很多原因:

  • 在调用中并不总是很明显哪些参数是ins而哪些是out
  • 你通常必须创建一个局部变量来捕获结果,而返回值可以内联使用(这可能是也可能不是一个好主意,但至少你可以选择)
  • 对我来说,对一个功能有一个“门”和“外门”似乎更干净 - 所有输入都在这里,所有输出都出现在那里
  • 我喜欢让我的论点列表尽可能短

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

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

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;

我认为这更清楚。

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

答案 3 :(得分:22)

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本质上是你的结构解决方案,但已经为你定义,并且随时可以适应任何两种数据类型。

答案 4 :(得分:15)

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

  • 如果它们与您的分数示例相关,那么我将使用结构或类实例。
  • 如果它们没有真正相关且无法分组到类/结构中,那么您可能应该将您的方法重构为两个。
  • 根据您返回的值的内存大小,您可能希望返回指向类实例或结构的指针,或使用引用参数。

答案 5 :(得分:12)

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

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

我知道你的例子只是一个“例子”,但事实是,除非你的函数比任何函数都要做的更多,如果你想要它返回多个值,你几乎肯定会错过一个对象。 / p>

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

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

答案 6 :(得分:10)

使用来自div的{​​{1}},ldiv(以及C99,lldiv)函数返回C(以及C ++)标准中的结构的先例(或<stdlib.h>)。

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

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

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

答案 7 :(得分:10)

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

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 struct variadic。更多细节:

Returning variadic aggregates (struct) and syntax for C++17 variadic template 'construction deduction guide'

答案 8 :(得分:5)

使用结构或类作为返回值。使用std::pair现在可能有效,但

  1. 如果您稍后决定要返回更多信息,那就不灵活了;
  2. 从标题中的函数声明中返回的内容以及以什么顺序返回的内容不是很清楚。
  3. 对于使用您的函数的任何人来说,返回具有自记录成员变量名称的结构可能不会出错。把我的同事戴上帽子,你的divide_result结构很容易让我,你的功能的潜在用户,在2秒后立即理解。使用输出参数或神秘对和元组进行混乱会花费更多时间来阅读并且可能使用不当。甚至在使用该函数几次后,我仍然不记得参数的正确顺序。

答案 9 :(得分:4)

如果你的函数通过引用返回一个值,编译器在调用其他函数时不能将它存储在寄存器中,因为理论上,第一个函数可以保存在全局可访问变量中传递给它的变量的地址,以及任何子函数被调用的函数可能会改变它,因此编译器将(1)在调用其他函数之前将寄存器中的值保存回内存;(2)在任何此类调用之后再次从内存中重新读取它时重新读取它。

如果您通过引用返回,您的程序优化将受到影响

答案 10 :(得分:4)

在这里,我正在编写一个在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可以返回两个以上的值。

答案 11 :(得分:3)

我倾向于在这样的函数中使用out-vals,因为我坚持使用返回成功/错误代码的函数的范例,我喜欢保持统一。

答案 12 :(得分:3)

有很多方法可以返回多个参数。我要兴奋了。

使用参考参数:

void foo( int& result, int& other_result );

使用指针参数:

void foo( int* result, int* other_result );

其优点是您必须在呼叫站点执行&,可能会警告人们这是一个超出参数的范围。

编写模板并使用它:

template<class T>
struct out {
  std::function<void(T)> target;
  out(T* t):target([t](T&& in){ *t = std::move(in); }) {}
  out(std::aligned_storage_t<sizeof(T), alignof(T)>* t):
    target([t](T&& in){ ::new( (void*)t ) T(std::move(in)); } ) {}
  template<class...Args>
  void emplace(Args&&...args) {
    target( T(std::forward<Args>(args)...) );
  }
  template<class X>
  void operator=(X&&x){ emplace(std::forward<X>(x)); }
  template<class...Args>
  void operator()(Args...&&args){ emplace(std::forward<Args>(args)...); }
};

然后我们可以做:

void foo( out<int> result, out<int> other_result )

一切都很好。 foo不再能够读取任何作为奖励传入的值。

定义可放入数据的位置的其他方法可用于构造out。例如,将东西放置在某个地方的回调。

我们可以返回一个结构:

struct foo_r { int result; int other_result; };
foo_r foo();

whick在C ++的每个版本中都可以正常工作,在中,这还允许:

auto&&[result, other_result]=foo();

零成本。由于保证了精确性,甚至甚至无法移动参数。

我们可以返回一个std::tuple

std::tuple<int, int> foo();

的缺点是未命名参数。这允许

auto&&[result, other_result]=foo();

也是。在之前,我们可以执行以下操作:

int result, other_result;
std::tie(result, other_result) = foo();

这有点尴尬。但是,保证省略在这里不起作用。

进入陌生地区(这是在out<>之后!),我们可以使用延续传递样式:

void foo( std::function<void(int result, int other_result)> );

现在呼叫者可以这样做:

foo( [&](int result, int other_result) {
  /* code */
} );

这种样式的一个好处是您可以返回任意数量的值(具有统一类型)而无需管理内存:

void get_all_values( std::function<void(int)> value )

value可能会调用get_all_values( [&](int value){} )回调函数500次。

出于纯粹的疯狂,您甚至可以在延续上使用延续。

void foo( std::function<void(int, std::function<void(int)>)> result );

其用法如下:

foo( [&](int result, auto&& other){ other([&](int other){
  /* code */
}) });

这将允许resultother之间存在多对一的关系。

再次使用统一值,我们可以这样做:

void foo( std::function< void(span<int>) > results )

在这里,我们用一系列结果调用回调。我们甚至可以重复执行此操作。

使用此功能,您可以有效地传递兆字节的数据,而无需在堆栈外进行任何分配。

void foo( std::function< void(span<int>) > results ) {
  int local_buffer[1024];
  std::size_t used = 0;
  auto send_data=[&]{
    if (!used) return;
    results({ local_buffer, used });
    used = 0;
  };
  auto add_datum=[&](int x){
    local_buffer[used] = x;
    ++used;
    if (used == 1024) send_data();
  };
  auto add_data=[&](gsl::span<int const> xs) {
    for (auto x:xs) add_datum(x);
  };
  for (int i = 0; i < 7+(1<<20); ++i) {
    add_datum(i);
  }
  send_data(); // any leftover
}

现在,std::function有点繁重,因为我们将在零开销的无分配环境中进行此操作。因此,我们希望一个永远不会分配的function_view

另一个解决方案是:

std::function<void(std::function<void(int result, int other_result)>)> foo(int input);

foo而不是使用回调并调用它,而是返回一个使用回调的函数。

foo(7)([&](int结果,int other_result){/ *代码* /}); 通过用单独的括号将输出参数与输入参数分开。

使用variant协程,可以使foo生成返回类型(或仅返回类型)的变体。语法尚不固定,因此我不会给出示例。

在信号和插槽的世界中,一个公开一组信号的函数:

template<class...Args>
struct broadcaster;

broadcaster<int, int> foo();

允许您创建一个foo,它可以异步工作并在完成后广播结果。

在这行代码下,我们有多种流水线技术,其中函数不执行任何操作,而是以某种方式安排数据的连接,并且操作相对独立。

foo( int_source )( int_dest1, int_dest2 );

然后,该代码在int_source包含整数才能提供之前不会执行任何操作。完成后,int_dest1int_dest2开始接收结果。

答案 13 :(得分:2)

我会说没有首选方法,这取决于你将如何处理响应。如果结果将在进一步处理中一起使用,那么结构是有意义的,如果不是,我倾向于作为单独的引用传递,除非该函数将在复合语句中使用:

x = divide( x, y, z ) + divide( a, b, c );

我经常选择在参数列表中通过引用传递'out结构',而不是通过返回新结构的复制开销来传递(但这会让小东西大汗淋漓)。

void divide(int dividend, int divisor, Answer &ans)

参数混乱吗?作为参考发送的参数表明该值将发生变化(与const引用相反)。明智的命名也可以消除混淆。

答案 14 :(得分:2)

备选方案包括数组,generatorsinversion of control,但这里没有一个是合适的。

一些(例如历史Win32中的Microsoft)倾向于使用引用参数来简化,因为很清楚谁分配以及它在堆栈中的外观,减少了结构的扩散,并允许单独的返回值来获得成功。

“纯粹”程序员更喜欢结构,假设它函数值(就像这里的情况一样),而不是函数偶然触及的东西。如果你有一个更复杂的程序,或者有状态的东西,你可能会使用引用(假设你有理由不使用类)。

答案 15 :(得分:2)

为什么你坚持使用多个返回值的函数?使用OOP,您可以使用提供具有单个返回值的常规函数​​的类,以及任意数量的其他&#34;返回值&#34;如下。优点是调用者可以选择查看额外的数据成员,但不需要这样做。这是复杂数据库或网络调用的首选方法,如果发生错误,可能需要大量额外的返回信息。

要回答原始问题,此示例有一个返回商的方法,这是大多数调用者可能需要的方法,此外,在方法调用之后,您可以将余数作为数据成员。

class div{
   public:
      int remainder;

      int quotient(int dividend, int divisor){
         remainder = ...;
         return ...;
      }
};

答案 16 :(得分:2)

而不是返回多个值,只需返回其中一个值,并在所需函数中引用其他值,例如:

int divide(int a,int b,int quo,int &rem)

答案 17 :(得分:1)

对于从函数返回多个值的通用系统,Boost元组将是我的首选。

可能的例子:

include "boost/tuple/tuple.hpp"

tuple <int,int> divide( int dividend,int divisor ) 

{
  return make_tuple(dividend / divisor,dividend % divisor )
}

答案 18 :(得分:1)

我们可以声明函数,它返回一个结构类型用户定义变量或指向它的指针。通过结构的属性,我们知道C中的结构可以包含多个非对称类型的值(即一个int变量,四个char变量,两个float变量等等......)

答案 19 :(得分:1)

如果只是一些返回值,我只是通过引用来完成,但是对于更复杂的类型,您也可以像这样:

static struct SomeReturnType {int a,b,c; string str;} SomeFunction()
{
  return {1,2,3,string("hello world")}; // make sure you return values in the right order!
}

如果仅是临时返回类型,请使用“静态”将返回类型的范围限制为此编译单元。

 SomeReturnType st = SomeFunction();
 cout << "a "   << st.a << endl;
 cout << "b "   << st.b << endl;
 cout << "c "   << st.c << endl;
 cout << "str " << st.str << endl;

这绝对不是最漂亮的方法,但它会起作用。

答案 20 :(得分:-1)

快速解答:

#include <iostream>
using namespace std;

// different values of [operate] can return different number.
int yourFunction(int a, int b, int operate)
{
    a = 1;
    b = 2;

    if (operate== 1)
    {
        return a;
    }
    else
    {
        return b;
    }
}

int main()
{
    int a, b;

    a = yourFunction(a, b, 1); // get return 1
    b = yourFunction(a, b, 2); // get return 2

    return 0;
}

答案 21 :(得分:-2)

以下是此类问题解决方案的完整示例

#include <bits/stdc++.h>
using namespace std;
pair<int,int> solve(int brr[],int n)
{
    sort(brr,brr+n);

    return {brr[0],brr[n-1]};
}

int main()
{
    int n;
    cin >> n;
    int arr[n];
    for(int i=0; i<n; i++)
    {
        cin >> arr[i];
    }

    pair<int,int> o=solve(arr,n);
    cout << o.first << " " << o.second << endl;

    return 0;
}
相关问题