質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.09%

C++ - 関数の返り値が2つ以上ある場合は tuple より引数経由のほうがよい?

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 7,795

OpenCV総合1位

環境 

  • C++11
  • Visual Studio 2017

質問内容

返り値が2つ以上の場合、Python では tuple を使って返すのが一般的ですが、C++ のライブラリ (例: OpenCV) の関数を見ていると、引数経由で返すものが多く、tuple を使うものはほぼ見かけません。

引数経由だと入力と出力がわかりづらくなるので、可読性の面ではタプルで返すほうが直感的のような気がします。

C++ の場合は、タプルで複数の返り値を返すということは、パフォーマンス等の面で不利になってしまう等のなんらかの事情があり、非推奨なのでしょうか?

#include <iostream>
#include <tuple>


// 引数で渡すタイプ
void divmod1(int a, int b, int &q, int &r)
{
    q = a / b;
    r = a % b;
}

// 返り値で渡すタイプ
std::tuple<int, int> divmod2(int a, int b)
{
    return std::make_tuple(a / b, a % b);
}

int main()
{
    int q, r;

    divmod1(10, 3, q, r);
    std::cout << "q: " << q << ", r: " << r << std::endl;
    // q: 3, r: 1

    std::tie(q, r) = divmod2(10, 3);
    std::cout << "q: " << q << ", r: " << r << std::endl;
    // q: 3, r: 1
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+3

こんにちは。

ちょっと前まで(とは言っても2011年頃ですから、既に結構たってます)、C++のSTLにstd::tuppleが無かったからと思います。今も、C++11非対応なコンパイラも生きていると思いますので、OpenCVは新しい機能への対応をわざわざやっていないということかも知れないです。

今なら(C++17以降)であれば、構造化束縛が便利そうです。(私はまだ使ったことはないのですが。)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/04/02 23:24

    迅速がご回答ありがとうございます。
    互換性等の理由からあまり見かけないのですね。
    ご紹介いただいた構造化束縛は、便利そうなので是非使ってみたいです。

    キャンセル

+2

C++標準ライブラリにタプル型(std::tuple)が導入されたのはC++11以降と比較的新しいため、OpenCVのように歴史の長いライブラリでは積極的に切り替えてまでは利用されていません。
(OpenCVはもともとC言語APIから始まったほど歴史の古いライブラリですから、保守的にAPIを移行しているようです。最新のOpenCV 4系でようやくC APIが廃止されました。)

引数経由だと入力と出力がわかりづらくなるので、可読性の面ではタプルで返すほうが直感的のような気がします。 

はい。最新のC++17では 構造化束縛(structured bindings) も導入されたため、Python風の多値返却も可読性高く記述できるようになっています。

#include <string>
#include <tuple>

// タプル(int型, string型, double型)を返す関数
std::tuple<int, std::string, double> func();

// 構造化束縛により戻り値タプルから個別変数へ代入
auto [vi, s, vd] = func();
// vi := int型変数
// s  := string型変数
// vd := double型変数

C++ Core Guildlinesでも F.21: To return multiple "out" values, prefer returning a struct or tuple にてタプル(や構造体)による多値返却スタイルを推奨しています。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/04/02 23:23

    迅速がご回答ありがとうございます。
    互換性等の理由からあまり見かけないのですね。
    構造化束縛は python-like にかけて良さそうですね。

    キャンセル

+2

解決済みですが。C++の中では、tuple自体新しいものなので戻り値に使うという例自体多くは見ません。

参考までに

今時のC++の中でどうなるかはわかりませんけど、参照を使って引数経由で値を返すというのは2つ目的があります。一つはご提示の通り、複数の場合に対応するためです。ただし、もう一つ意味があります。

少なくともCやアセンブラでは大きなサイズの構造体(相当)データを値渡しすることは望ましくありません。このために、参照経由としているケースがあります。

というのも、高級言語では簡単に記述できても、機械語レベルで考えると結構な手間になりえるためです。例えば、構造体のデータサイズなんて簡単に大きくできます。一方、CPUが一度に処理できるデータサイズは基本的にレジスタに収まる範囲だけです。32bitCPUで考えれば、4kバイトの領域をコピーするために1k回の反復処理が内部的に行われるわけです。ですから、値渡しでなく参照渡し記述とした結果引数経由となる傾向にあります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/04/02 23:55

    ご回答ありがとうございます。
    ハードウェアの観点から考えたことはなかったので、とても参考になります。
    情報ありがとうございました。

    キャンセル

  • 2019/04/03 04:27

    HogeAnimalLoverさん
    RVO、NRVOに対応しているC++コンパイラが結構多いそうです。
    https://blog.kmc.gr.jp/entry/2014/12/20/231430
    この最適化が有効であれば巨大なデータを効率よく返却できます。なので、多少大きめのインスタンスでも戻り値で返却することが意外に多いです。(RVO/NRVOを知った時、かなり衝撃でした。)

    更にC++17でコピー無しを保証する仕様が導入されているそうです。
    https://faithandbrave.hateblo.jp/entry/2017/01/24/161342

    キャンセル

  • 2019/04/03 23:48 編集

    Chironianさん
    ご指摘ありがとうございます。「今時のC++ならば多分そのようなものはあるだろう」と思ってはいましたが、やはり実在するのですね。調査不足でした。それにしてもC++は0x11以降まるでついていけていないなOTL

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.09%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る