C ++でのタプルの使用が一般的ではないのはなぜですか?


124

C ++でBoost Tuple LibraryまたはTR1の標準ライブラリのいずれかでタプルを使用しているように思われないのはなぜですか?多くのC ++コードを読みましたが、タプルの使用をめったに見ませんが、多くの場合、タプルが多くの問題(通常は関数から複数の値を返す)を解決する場所がたくさんあります。

タプルを使用すると、次のようなあらゆる種類の優れた操作を実行できます。

tie(a,b) = make_tuple(b,a); //swap a and b

それは確かにこれよりも優れています:

temp=a;
a=b;
b=temp;

もちろん、いつでもこれを行うことができます:

swap(a,b);

しかし、3つの値をローテーションする場合はどうでしょうか。あなたはタプルでこれを行うことができます:

tie(a,b,c) = make_tuple(b,c,a);

タプルを使用すると、関数から複数の変数を返すのがはるかに簡単になります。これは、値をスワップするよりもはるかに一般的なケースです。参照を使用して値を返すことは、確かにあまりエレガントではありません。

タプルに私が考えていない大きな欠点はありますか?そうでない場合、なぜそれらがめったに使用されないのですか?彼らは遅いですか?それとも人々が彼らに慣れていないだけですか?タプルを使用するのは良い考えですか?


17
+1は巧妙なタプルスワッピングトリックです:)
kizzx2

10
a = a ^ b; b = a ^ b; a = a ^ b;
Gerardo Marset、2011

3
IMOタプルは、弱いタイピング言語またはネイティブ構造である言語で便利です。たとえば、PythonやPHPでは生活を楽にするだけですが、C ++では型付けが多すぎ(テンプレートから構築するには)、利点が少なすぎます。
2013年

4
OPへのコメント:現在受け入れられている回答は、事実上誤りであるという点ですでに廃止されていると思います。受け入れられた回答の選択を再検討する必要がある場合があります。
ulidtko 2014

8
@GerardoMarsetあなたは本気ですか?
thesaint 2015年

回答:


43

それはまだ標準ではないので。非標準のものには、はるかに高いハードルがあります。プログラマーが熱望しているため、Boostの一部が人気を博しています。(hash_mapが思い浮かびます)。しかしタプルは便利ですが、人々がそれを気にするのはそれほど圧倒的で明確な勝利ではありません。


1
人々は、Boostの他の部分をクレイジーに使用しているようです。確かにハッシュマップはタプルよりも一般的にはるかに有用です。
Zifre 2009年

あなたが見ているものの詳細は分かりませんが、人々がクレイジーに使用しているパーツは、彼らが本当に望んでいた機能であると思います。したがって、(再び、推測して)ハッシュマップ、カウントされたポインタなどの人気度。タプルは便利ですが、穴を埋めるために飛び出すものではありません。見返りは明らかではありません。どのくらいの頻度で正確にN個のオブジェクトを回転する必要がありますか?(任意に長いベクトルを回転する必要があるのとは対照的に)。そして人々は参照によって戻り値を渡すか、小さなクラスや構造体を返すことに慣れています。
アランデスメット

20
それは実際には現在C ++ 11標準の一部です:en.cppreference.com/w/cpp/utility/tuple
Roman Susi

124

皮肉な答えは、多くの人々がC ++でプログラミングしているが、より高いレベルの機能を理解したり使用したりしていないということです。許可されないことが原因である場合もありますが、多くの場合、単に試していない(または理解していない)だけです。

非ブーストの例として、何人の人がにある機能を使用してい<algorithm>ますか?

言い換えれば、多くのC ++プログラマーは、C ++コンパイラーを使用する単なるCプログラマーであり、おそらくstd::vectorおよびstd::listです。これが、の使用がboost::tuple一般的でない理由の1つです。


18
C ++プログラマーは、この答えが音を立てるほど馬鹿ではないので、私からは-1です。
user541686 2014年

5
@Mehrdad大量のC ++資料を読んで、商用および非商用の両方のC ++コードを調べた結果、「C ++」開発者の非常に大部分は、純粋なCの開発者ではなかったと言っても、かなり安全だと思います。 Cコンパイラ。たとえば、テンプレートはほとんどの素材からほとんど完全に欠けています(私が多くを愛することを学んだもの)。奇妙なマクロのハックが一般的で、名前空間はひどく活用されていません。
2014年

5
ばかげた答え。必要な場合は、追いつきます。それらは必要ありません。そのため、使用されません。わかりにくいので使わないというのは悪い。
マイケル・チョルダキス2014年

9
@Michael Nonsenseコメント。言語の完全なサブセットのチューリングを取得したら、プログラミングには何も必要ありません。使用の欠如は、誰もがより高いレベルのC ++構造を理解し、それらを使用しないことを選択したことを意味するものではありません。
Trey Jackson

4
Tbh可変テンプレートメタプログラミング以外でstd :: tupleが必要になることはありませんでした。「タプルさえ持っていれば」と悲しそうな顔をして腰を下ろすような生活はありませんでした。実際、私がタプルを見るとき、私は「誰か正気がそれらに何を必要としているのか(私は自分自身を正気だとは思わないでしょう)」と思います。メタプログラミング以外の人々は、それらを「匿名の構造体」として使用しているようです。これは非常に醜く、コードの品質と保守性が頭の中に欠如していることを示しています。
thesaint 2015年

23

C ++タプル構文は、ほとんどの人が望むよりもかなり冗長になる可能性があります。

検討してください:

typedef boost::tuple<MyClass1,MyClass2,MyClass3> MyTuple;

したがって、タプルを広範に使用したい場合は、どこでもタプルのtypedefを取得するか、どこでも煩わしいほど長い型名を取得します。タプルが好きです。必要なときに使います。ただし、通常は、N要素のインデックスのように、またはマルチマップを使用して範囲イテレータのペアを結合する場合など、いくつかの状況に限定されます。そして、それは通常非常に限られた範囲にあります。

HaskellやPythonのようなものと比較すると、見た目は非常に醜くハックです。C ++ 0xがここに来て「auto」キーワードを取得すると、タプルはより魅力的に見え始めます。

タプルの有用性は、それらを宣言、パック、アンパックするために必要なキーストロークの数に反比例します。


ほとんどの人は「名前空間ブーストを使用して」行います。boost ::と入力する必要はありません。タプルのタイピングはそれほど問題ではないと思います。そうは言っても、あなたにはポイントがあると思います。autoは、より多くの人々にタプルの使用を開始させるかもしれません。
Zifre 2009年

2
@Zifre:問題は、ネームスペースの汚染を強制し、ネームスペースを破壊するため、ヘッダーファイル内で「ネームスペースXを使用」してはならないことです。
Fooz氏、2009年

1
ああ、はい、ヘッダーのことを忘れています。ただし、プログラムコード内では、そのことを心配する必要はありません。C ++ 0xを取得したら、autoを使用できます。これにより、入力の多くが不要になります。
Zifre 2009年

19
私だけでしょうか?私は "boost ::"の7文字を入力するのを節約するのではなく、他の33文字を参照していると思います。これは、特にそれらも名前空間スコープである場合、多くのクラス名入力の問題です。ばかげた例として、boost :: tuple <std :: string、std :: set <std :: string>、std :: vector <My :: Scoped :: LongishTypeName>>を取り上げます。
Ogre Psalm33 2011年

10

私にとっては、それは習慣です。タプルは私にとって新しい問題を解決しません。私がすでにうまく処理できる数個だけです。値を交換することで、昔ながらの方法がまだ簡単に感じられます。さらに重要なのは、「より良い」交換方法について本当に考えていないことです。現状のままで十分です。

個人的には、タプルは複数の値を返すための優れたソリューションではないと思いますstruct-sの仕事のように聞こえます。


4
「私は「より良い」交換方法について本当に考えていません。」-コードを書くとき、私はバグを書きます。コードの複雑さを減らすことで、私が書くバグの数が減ります。私は同じバグを何度も作成するのが嫌いです。はい、コードを<strike>スワップ</>する方法を考えています。より少ない可動部分(LOC、一時変数、タイプミスの識別子)、より読みやすいコード。良いコード。
sehe

同意します。自動ポインタまたはスマートポインタでラップされたクラスは、型保存されます。タプルを1回使用しましたが、クラスを使用してコードを書き直しました。retValue.stateはretValue.get <0>()よりも明確です。
Valentin Heinitz、2014年

1
@sehe:私の目標は、より良く、より読みやすいコードを書くことでもあります。より多くのタイプの構文を追加するとコストがかかります。「より良いスワッピング」が、読み取るコードの各行でさらに多くのタイプの構文を考えるという精神的なオーバーヘッドを正当化すると私は信じていません。
ojrac

8

しかし、3つの値をローテーションする場合はどうでしょうか。

swap(a,b);
swap(b,c);  // I knew those permutation theory lectures would come in handy.

OK、したがって4などの値を使用すると、最終的にnタプルはn-1スワップよりもコードが少なくなります。デフォルトのスワップでは、3サイクルのテンプレートを自分で実装した場合の4つの割り当てではなく、6つの割り当てが行われますが、コンパイラーが単純な型に対して解決することを願っています。

スワップが扱いにくいまたは不適切なシナリオを考え出すことができます。次に例を示します。

tie(a,b,c) = make_tuple(b*c,a*c,a*b);

開梱するのは少し厄介です。

ただし、タプルが適している最も一般的な状況に対処する既知の方法があるため、タプルを使用する緊急性は高くありません。他に何もない場合、私は自信がない:

tie(a,b,c) = make_tuple(b,c,a);

6つのコピーを行わないため、一部のタイプにはまったく不適切です(コレクションが最も明白です)。タプルは「大きな」タイプには良いアイデアだと私に納得してください。これはそうではないと言います:-)

複数の値を返す場合、値の型に互換性がない場合はタプルは完璧ですが、呼び出し元が誤った順序で取得する可能性がある場合は、タプルが気に入らない人もいます。一部の人々は、複数の戻り値がまったく好きではなく、それらをより簡単にすることによってそれらの使用を奨励したくありません。一部の人々は、入力パラメータと出力パラメータに名前付き構造体を好むだけで、おそらく野球のバットでタプルを使用するように説得することはできませんでした。味の説明はありません。


1
ベクトルをタプルと交換したくはないでしょう。3つの要素を交換する方が、2つの交換よりもタプルを使用する方が明らかに明確だと思います。複数の戻り値については、outパラメータは悪であり、構造体は余分な型指定であり、複数の戻り値が必要な場合は確かにあります。
Zifre 2009年

なぜタプル使用するのか知っている(そして、機会があれば私がそれらを使用する理由を私は知っているが、今までそうは思わなかった)。気づいても他の人が使わないのはなぜかと思います。彼らが反対するので、例えば...「アウトparamsが悪です」
スティーブ・ジェソップ

「tie(a、b、c)= make_tuple(b、c、a);」を置き換えることができるかどうか知っていますか。「tie(a、b、c)= tie(b、c、a);」による ?
Rexxar、2009年

2
ネクタイ(技術的にはティア)は、非const参照で作成されたタプルです。関係する参照の一部が同じ参照を持っている場合に、operator =とtie / tupleのコピーコンストラクターが何を保証するかを説明しているboostドキュメントが見つかりません。しかし、それはあなたが知る必要があることです。operator =の素朴な実装は明らかに非常にうまくいかない可能性があります...
スティーブジェソップ

1
@Steve:ネクタイはすべてコピー防止に関するものであるため(コピー不可能なタイプで機能するはずです。LHSは完全に別のものである可能性があることに注意してください)、実際にはすべてが非常に間違っているはずです(非PODクラスオブジェクトを考えてください)。tempを使用せずに同じロジックを作成する方法を想像してみてください。
sehe

7

多くの人が指摘したように、タプルは他の機能ほど便利ではありません。

  1. 入れ替えと回転の仕掛けは単なる仕掛けです。彼らはこれまでに見たことのない人を完全に混乱させており、それはほとんど全員であるため、これらの仕掛けはソフトウェアエンジニアリングの実践としては不十分です。

  2. タプルを使用して複数の値を返すことは、代替方法よりも自己文書化がはるかに少なく、名前付き型を返すか、名前付き参照を使用します。この自己文書化を使用しないと、戻り値が相互に変換可能であり、賢くならない場合、戻り値の順序を混同しやすくなります。


6

誰もがブーストを使用できるわけではなく、TR1はまだ広く利用できません。


3
多くの人がBoostを使用しています。それらの人々はタプルも使用している可能性があります。
Zifre 2009年

3
あなたはなぜ人々がそれらを使わないのかと尋ねました、そして私は一つの答えを出しました。
ブライアンニール

2
反対投票者へ:私はブーストを使用することが政治的に不可能である場所でたまたま働いており、現在でも、私たちが使用するコンパイラーツールチェーン(組み込みシステム用)はTR1 / C ++ 11をサポートしていません。
Brian Neal

5

組み込みシステムでC ++を使用する場合、Boostライブラリの取り込みは複雑になります。それらは互いに結合するため、ライブラリーのサイズが大きくなります。データ構造を返すか、タプルの代わりにパラメーターの受け渡しを使用します。Pythonでタプルを返す場合、データ構造は返される値の順序と型であり、明示的ではありません。


5

よく設計されたコードは通常それらを必要としないため、めったにそれらを目にすることはありません-匿名の構造体を使用することは、名前付きのものを使用することよりも優れている場合がほとんどありません。タプルが実際に表すのはすべて匿名の構造体であるため、ほとんどの状況でほとんどのコーダーは本物をそのまま使用します。

タプルの戻りが意味のある関数 "f"があるとします。一般的なルールとして、このような関数は通常、失敗する可能性があるほど複雑です。

「f」が失敗した場合、ステータスを返す必要があります。結局のところ、呼び出し元がすべてのパラメータを調べて失敗を検出する必要はありません。"f"はおそらくパターンに適合します:

struct ReturnInts ( int y,z; }
bool f(int x, ReturnInts& vals);

int x = 0;
ReturnInts vals;
if(!f(x, vals)) {
    ..report error..
    ..error handling/return...
}

それはきれいではありませんが、代替案がどれほど醜いか見てください。まだステータス値が必要ですが、コードは読みにくく、短くはないことに注意してください。タプルで1コピーのコストが発生するので、おそらくもっと遅くなります。

std::tuple<int, int, bool> f(int x);
int x = 0;
std::tuple<int, int, bool> result = f(x); // or "auto result = f(x)"
if(!result.get<2>()) {
    ... report error, error handling ...
}

別の重要な欠点がここに隠されています。 "ReturnInts"を使用すると、 "f"のインターフェイスを変更せずに "ReturnInts"を変更して、 "f"のリターンを追加できます。タプルソリューションはその重要な機能を提供していないため、どのライブラリコードにも劣る回答になります。


1
例外により、そのインターフェースはより明確になります。
David Stone

公平に(そしてパーティーに極端に遅れる)using std::tuple;ためにtupleは、コードを設定して使用するだけで読みやすくすることができます。
Alrekr

2
を使用tupleすると、コード読みにくくなります。最近のほとんどのコードには、非常に多くのシンボルが含まれてstd::tupleいます。見ると、それが何であるかがはっきりとわかります。
トムスワーリー2016

3

確かにタプルは便利ですが、前述のように、オーバーヘッドが少しあり、実際に使用する前に、1つまたは2つのハードルをジャンプする必要があります。

複数の値を返す必要がある場所や、いくつかの値を交換する必要がある場所をプログラムが一貫して見つけている場合は、タプルルートを使用する価値がありますが、それ以外の場合は、従来の方法で行う方が簡単な場合もあります。

一般的に言って、誰もがすでにBoostをインストールしているわけではないので、ダウンロードしてインクルードディレクトリをタプル機能のためだけに動作するように構成するという煩わしさはありません。Boostを既に使用している人は、Boostを使用していないユーザーよりもプログラムでタプルの使用を見つける可能性が高く、他の言語からの移民(Pythonが頭に浮かぶ)は、タプルの欠如に単純に気分を害する可能性が高いことがわかりますC ++ではタプルサポートを追加する方法を探索するよりも。


1

データストアにstd::tuplestructと配列の両方の最悪の特性があるため、すべてのアクセスはn番目の位置に基づいていますがtupleforループを使用して反復することはできません。

したがって、要素tupleが概念的に配列である場合は配列を使用し、要素が概念的に配列でない場合は、構造体(名前付き要素を持つ)の方が保守しやすくなります。(a.lastnameはより説明的ですstd::get<1>(a))。

これにより、OPによって言及された変換は、タプルの唯一の実行可能なユースケースとして残ります。


0

多くの人がBoost.Tupleの代わりにBoost.AnyとBoost.Variantを(何らかのエンジニアリングを行って)使用しているように感じます。


なぜ効率的な静的型付けをそのようなものと交換するのですか?
Zifre 2009年

Boost.Variantは完全にタイプセーフです。
user21714 2009年

1
おっと、はい、タイプセーフですが、実行時のタイピングを行います。
Zifre 2009年

5
タプルをAny / Variantに置き換える方法がわかりません。彼らは同じことをしません。
Mankarse 2011

1
@Zifre私は作者のために話すことはできませんが、ここでの含意はそれらを他のコンテナタイプと一緒に使用することだと思います。
Tim Seguine 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.