C ++関数から複数の値を返す


242

C ++関数から複数の値を返す推奨方法はありますか?たとえば、2つの整数を除算し、商と剰余の両方を返す関数を想像してみてください。私がよく目にする1つの方法は、参照パラメーターを使用することです。

void divide(int dividend, int divisor, int& quotient, int& remainder);

バリエーションは、1つの値を返し、もう1つを参照パラメーターを介して渡すことです。

int divide(int dividend, int divisor, int& remainder);

別の方法は、すべての結果を含む構造体を宣言し、それを返すことです。

struct divide_result {
    int quotient;
    int remainder;
};

divide_result divide(int dividend, int divisor);

これらの方法のいずれかが一般的に推奨されますか、それとも他の提案がありますか?

編集:実際のコードでは、結果が3つ以上ある場合があります。彼らはまた、異なるタイプであるかもしれません。

回答:


216

2つの値を返すには、std::pair(通常はtypedefされた)を使用します。3 つ以上の結果が返されるかどうかboost::tuple(C ++ 11以降ではstd::tuple)を確認する必要があります。

C ++ 17での構造化バインディングの導入により、リターンstd::tupleはおそらく受け入れられる標準になるはずです。


12
タプルの場合は+1。ラージオブジェクトが構造体で返される場合と参照渡しされる場合のパフォーマンスの違いに注意してください。
Marcin

12
タプルを使用する場合は、ペアにも使用してください。なぜ特別なケースがあるのですか?
Ferruccio

4
フレッド、はいboost :: tupleがそれを行うことができます:)
Johannes Schaub-litb 2008年

46
C ++ 11では、を使用できますstd::tuple
Ferruccio 2011年

14
関数から複数の値を受け入れる場合、これを行う便利な方法は、std::tie stackoverflow.com
a / 2573822/502144

175

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

4
タプルを返す関数に1つ懸念があります。上記の関数プロトタイプがヘッダーにあるとしたら、関数定義を理解せずに、最初と2番目の戻り値の意味を知るにはどうすればよいですか?商剰余または剰余商。
Uchia Itachi 2016

7
@UchiaItachi関数パラメーターについても同じように、名前を付けることができますが、言語はそれを強制することもありません。パラメーター名は、読み取り時に呼び出しサイトで値を持ちません。また、1回の戻りでは、型は1つだけですが、名前を付けると便利です。タプルを使用すると、問題が2倍になるので、言語だけでなく、いくつかの方法で自己文書化することに関して言語に欠けています。
pepper_chico

1
最後の例で、divide()の戻り値の型を明示的に指定したい場合はどうなりますか?次に、別の場所で結果を定義しますか、それとも戻り値型の仕様で結果を定義できますか?
スラバ

1
あなたが関数のシグネチャのタイプの権利を定義することはできません@Slava、あなたはそれが正常に行われているように、型の外側を宣言し、戻り値の型としてそれを使用する必要があります(単に移動struct関数本体の外にラインをして交換autoして関数の戻り値をresult
pepper_chico

3
@pepper_chicoの関数定義をdivide別のcppファイルに入れたい場合はどうなりますか?エラーが発生しますerror: use of ‘auto divide(int, int)’ before deduction of ‘auto’。どうすればこれを解決できますか?
Adriaan

123

個人的に、私は多くの理由で戻りパラメーターを一般的に嫌います:

  • どのパラメーターが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;

これはもっとはっきりしていると思います。

ですから、私が最初に選択するのは、構造体のテクニックだと思います。ペア/タプルのアイデアは、場合によっては優れたソリューションとなる可能性があります。可能な場合は、戻りパラメーターを使用したくない。


1
申告する提案structなどはVelocity素晴らしいものです。ただし、1つの問題は、名前空間を汚染することです。C ++ 11では、struct型名が長くなり、を使用できるようになると思いますauto result = calculateResultingVelocity(...)
Hugues 2013

5
+1。関数は、何らかの順序付けられた「もののタプル」ではなく、1つの「もの」を返す必要があります。
DevSolar 2013年

1
この回答で説明されている理由により、私はstd :: pairs / std :: tuplesよりも構造体を好みます。しかし、名前空間の「汚染」も好きではありません。私にとって理想的な解決策は、のような匿名の構造体を返すことstruct { int a, b; } my_func();です。これは次のように使用できます:auto result = my_func();。しかし、C ++ではこれを許可していません。「戻り値の型で新しい型が定義されていない可能性があります」。したがって、私はstruct my_func_result_t...のような構造体を作成する必要があります
anton_rh

2
@anton_rh:C ++ 14ではでローカル型を返すことができるautoためauto result = my_func();、簡単に取得できます。
ildjarn 2017年

4
15年ほど前にブーストを発見したとき、それは非常に便利なため、タプルを頻繁に使用しました。時間がたつにつれて、特に同じタイプのタプル(たとえば、tuple <double、double>;どちらであるか)の可読性に問題が発生しました。したがって、最近では、少なくともメンバー変数の名前が何か意味のあることを示す小さなPOD構造を導入する傾向が強くなっています。
gast128 2017

24
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は基本的には構造体ソリューションですが、すでに定義されており、任意の2つのデータ型に適応する準備ができています。


3
それは私の簡単な例でうまくいきます。ただし、一般的には、3つ以上の値が返される場合があります。
フレッドラーソン、

5
また、自己文書化しません。DIVの残りのx86レジスタを覚えていますか?
マーク・

1
@Mark-位置ソリューションは保守性が低下する可能性があることに同意します。「permute and baffle」問題が発生する可能性があります。
フレッドラーソン、

16

実際の機能と複数の値の意味、およびそれらのサイズに完全に依存しています。

  • 分数の例のように関連している場合は、構造体またはクラスのインスタンスを使用します。
  • それらが実際に関連しておらず、クラス/構造体にグループ化できない場合は、おそらくメソッドを2つにリファクタリングする必要があります。
  • 返す値のメモリ内サイズに応じて、クラスインスタンスまたは構造体へのポインターを返すか、参照パラメーターを使用することができます。

1
私はあなたの答えが好きです、そしてあなたの最後の弾丸は私が読んだことを思い出します値ごと
セージ

12

これに対するオブジェクト指向のソリューションは、比率クラスを作成することです。追加のコードを必要とせず(いくつかを保存します)、大幅にクリーン/クリアになり、このクラス外のコードもクリーンアップできるようにするいくつかの追加のリファクタリングを提供します。

実際、私は誰かが構造を返すことを推奨していると思います。これは十分に近い構造ですが、これはコンストラクターといくつかのメソッドを含む完全に考え抜かれたクラスである必要があるという意図を隠しています。ペア)は、おそらくこのクラスのメンバーであり、それ自体のインスタンスを返します。

私はあなたの例が単なる「例」であったことを知っていますが、実際には、関数が他の関数よりも多くのことを実行していない限り、複数の値を返したい場合、ほぼ確実にオブジェクトが欠落しています。

小さな作業を行うためにこれらの小さなクラスを作成することを恐れないでください-それはOOの魔法です-すべてのメソッドが非常に小さくシンプルになり、すべてのクラスが小さくて理解可能になるまで、それを分解してしまいます。

何かが間違っていることを示すべきだったもう1つのこと:OOには基本的にデータがない-OOはデータを渡すことではなく、クラスは自身のデータを内部で管理および操作する必要があり、データの受け渡し(アクセサーを含む)何かを再考する必要があるかもしれないという兆候です。


10

C(およびC ++)標準ではdiv、(またはldivC99ではlldiv)関数を使用して<stdlib.h>(または<cstdlib>)ます。

「戻り値と戻りパラメータの組み合わせ」は、通常、最もクリーン度が低くなります。

関数がステータスを返し、戻りパラメーターを介してデータを返すことは、Cでは賢明です。例外を使用して代わりに障害情報をリレーできるC ++では、それほど明確ではありません。

戻り値が3つ以上ある場合は、おそらく構造のようなメカニズムが最適です。


10

C ++ 17を使用すると、1つ以上の移動不可/コピー不可の値を返すこともできます(特定の場合)。移動できない型を返す可能性は、新しい保証された戻り値の最適化によってもたらさます。これは、集計、およびテンプレートコンストラクターと呼ばれるものでうまく 構成されます。

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

このことについてはかなりのものは、それが発生しないことが保証されていることである任意のコピーまたは移動します。サンプルのmanystructをvariadicにすることもできます。詳細:

可変個の集計(構造体)とC ++ 17可変個のテンプレート「構築控除ガイド」の構文を返す


6

複数のパラメーターを返す方法はたくさんあります。私は疲れきっています。

参照パラメーターを使用します。

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

ポインタパラメータを使用します。

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

これには、&呼び出しサイトでaを実行する必要があるという利点があります。

テンプレートを作成して使用します。

template<class T>
struct out {
  std::function<void(T)> target;
  out(T* t):target([t](T&& in){ if (t) *t = std::move(in); }) {}
  out(std::optional<T>* t):target([t](T&& in){ if (t) t->emplace(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> // TODO: SFINAE enable_if test
  void emplace(Args&&...args) {
    target( T(std::forward<Args>(args)...) );
  }
  template<class X> // TODO: SFINAE enable_if test
  void operator=(X&&x){ emplace(std::forward<X>(x)); }
  template<class...Args> // TODO: SFINAE enable_if test
  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コールバックは、とき500回呼び出すことができget_all_values( [&](int value){} )

純粋な狂気の場合は、継続に継続を使用することもできます。

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

その用途は次のようになります:

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

との間の多対1の関係を許可resultotherます。

ここでもuniforn値を使用して、これを行うことができます。

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 result、int other_result){/ * code * /}); これは、別々の角括弧を持つことにより、入力パラメーターから出力パラメーターを分割します。

variantコルーチンでfooは、戻り値の型(または戻り値の型)のバリアントのジェネレーターを作成できます。構文はまだ固定されていないため、例は示しません。

シグナルとスロットの世界では、一連のシグナルを公開する関数:

template<class...Args>
struct broadcaster;

broadcaster<int, int> foo();

foo非同期で動作し、終了時に結果をブロードキャストするを作成できます。

このラインの下には、関数が何かを実行するのではなく、何らかの方法でデータが接続されるように調整するさまざまなパイプラインテクニックがあり、その実行は比較的独立しています。

foo( int_source )( int_dest1, int_dest2 );

その後、このコードはいないまでは何もint_sourceそれを提供するために、整数値を持っています。それが行われるint_dest1int_dest2、結果の受信を開始します。


この回答には他の回答よりも多くの情報が含まれています!特に、auto&&[result, other_result]=foo();タプルと構造体の両方を返すfor関数に関する情報。ありがとう!
jjmontes

特にC ++ 11に悩まされているため、他の人が提案する最新のソリューションの一部を使用できないため、この包括的な回答に感謝します。
GuyGizmo

5

戻り値には構造体またはクラスを使用します。std::pair今のところは使用できますが、

  1. 後でもっと多くの情報を返したい場合は柔軟性がありません。
  2. ヘッダーでの関数の宣言から、何が返され、どの順序で返されるかは明確ではありません。

自己文書化されたメンバー変数名を含む構造体を返すと、関数を使用する人にとってバグが発生しにくくなります。私の同僚の帽子を少し着て、あなたのdivide_result構造は私(あなたの機能の潜在的なユーザー)が2秒後にすぐに理解するのは簡単です。出力パラメータや不思議なペアやタプルをいじり回すと、読み取りに時間がかかり、正しく使用されない可能性があります。そして、おそらく関数を数回使用した後でも、引数の正しい順序を覚えていません。


4

関数が参照を介して値を返す場合、理論的には、最初の関数はそれに渡された変数のアドレスをグローバルにアクセス可能な変数に保存でき、その後に呼び出される関数はそれを変更すると、コンパイラーは、(1)他の関数を呼び出す前にレジスターからメモリーに値を保存し、(2)そのような呼び出しの後にメモリーから再び必要になったときにそれを再読み取りします。

参照によって戻る場合、プログラムの最適化は影響を受けます


4

ここでは、c ++で複数の値(3つ以上の値)を返すプログラムを書いています。このプログラムは、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 :: tupleは3つ以上の値を返すことができますが、std :: pairを使用すると2つの値のみを返すことができます。


4
C ++ 14では、auto戻り値の型をオンにcalしてこれをさらにきれいにすることもできます。(IMO)。
sfjac 2015

3

私はこのような関数でout-valを使用する傾向があります。成功/エラーコードを返す関数のパラダイムに固執し、物事を均一に保ちたいからです。


2

代替には、配列、ジェネレータ、および制御の反転が含まれますが、ここでは適切ではありません。

一部(たとえば、歴史的なWin32のMicrosoft)では、単純にするために参照パラメーターを使用する傾向があります。これは、誰が割り当て、どのようにスタックを見るかが明確であり、構造体の急増を減らし、成功するための個別の戻り値を許可するためです。

「純粋な」プログラマーは、構造体が偶然触れられるものではなく、関数の値である想定して(ここではそうです)、構造体を好みます。より複雑な手順や状態を伴うものがある場合は、おそらく参照を使用します(クラスを使用しない理由があると仮定します)。


2

私は好ましい方法はないと思います、それはすべてあなたが応答で何をしようとしているのかに依存します。結果がさらなる処理で一緒に使用される場合、構造は意味があります。そうでない場合は、関数が複合ステートメントで使用される場合を除いて、個別の参照として渡す傾向があります。

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

私はしばしば、新しい構造を返すオーバーヘッドのコピーを渡すのではなく、パラメーターリストで参照によって「出力構造」を渡すことを選択します(ただし、これは小さなものに汗を流しています)。

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

outパラメータは混乱しますか?参照として送信されたパラメーターは、値が変更されることを示唆しています(const参照とは対照的です)。賢明な名前を付けることで混乱も取り除かれます。


1
少しわかりにくいと思います。それを呼び出すコードを読んでいる人は、「divide(a、b、c);」を参照します。彼らが署名を調べるまで、cがoutvalであることを示すものはありません。しかし、それはこの質問に特定するのではなく、非const参照パラメーターに対する一般的な恐れです。
スティーブジェソップ

2

なぜ複数の戻り値を持つ関数を要求するのですか?OOPを使用すると、単一の戻り値と、以下のような追加の「戻り値」を含む通常の関数を提供するクラスを使用できます。利点は、呼び出し側が追加のデータメンバーを参照するかどうかを選択できることですが、これを行う必要はありません。これは、エラーが発生した場合に多くの追加の戻り情報が必要になる場合がある、複雑なデータベースまたはネットワーク呼び出しに適した方法です。

元の質問に答えるために、この例には、ほとんどの呼び出し元が必要とする可能性がある商を返すメソッドがあり、さらに、メソッド呼び出しの後に、残りをデータメンバーとして取得できます。

class div{
   public:
      int remainder;

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

1
これは非効率な場合があると思います。たとえば、複数の戻り値を生成する単一のforループがあるとします。これらの値を個別の関数に分割する場合は、値ごとに1回ループを実行する必要があります。
jiggunjer 2015年

1
@jiggunjerループを1回実行し、複数の戻り値を個別のクラスデータメンバーに格納できます。これは、OOPコンセプトの柔軟性を強調しています。
Roland

2

複数の値を返すのではなく、それらの1つを返し、必要な関数内で他の値を参照するだけです。

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

質問自体ではこれについて言及しませんでしたか?また、私の回答で私の異議を参照してください。
フレッド・ラーソン

1

Boost tupleは、関数から複数の値を返す一般化されたシステムにとって、私が好む選択です。

可能な例:

include "boost/tuple/tuple.hpp"

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

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

1

関数を宣言して、構造タイプのユーザー定義変数またはそのポインターを返すようにすることができます。そして、構造体の特性により、Cの構造体は非対称型の複数の値(つまり、1つのint変数、4つのchar変数、2つのfloat変数など)を保持できることがわかります。


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;

これは間違いなくそれを行うための最もきれいな方法ではありませんが、動作します。


-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;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.