web-dev-qa-db-ja.com

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

Boost Tuple Library またはTR1の標準ライブラリのいずれかで、C++でタプルを使用しないように見えるのはなぜですか?私は多くの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);

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

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

120
Zifre

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

40
Alan De Smet

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

非ブーストの例として:<algorithm>にある機能を使用する人は何人ですか?

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

122
Trey Jackson

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

考慮してください:

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

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

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

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

23
user21714

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

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

9
ojrac

しかし、3つの値を回転させたい場合はどうでしょうか?

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

わかりました。したがって、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つのコピーを行わないため、一部のタイプにはまったく不適切です(コレクションが最も明白です)。タプルは「大」型の良いアイデアであると私に説得してください、これはそうではないと言って:-)

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

8
Steve Jessop

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

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

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

7
igorlord

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

6
Brian Neal

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

5
DanM

適切に設計されたコードは通常それらを必要としないので、それらを見ることはほとんどありません。匿名の構造体を使用する方が名前付き構造体を使用するよりも優れているケースは多くありません。 Tupleが実際に表すものはすべて匿名の構造体であるため、ほとんどの状況でほとんどのコーダーは本物を使います。

Tupleの戻り値が意味をなす関数 "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...
}

それはきれいではありませんが、代替手段がどれほどいかを見てください。ステータス値が必要なことに注意してください。ただし、コードは読みにくく、短くなりません。 Tupleで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"の戻り値を変更できます。 Tupleソリューションは、その重要な機能を提供していないため、どのライブラリコードに対しても劣った答えになります。

4
Zack Yezek

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

プログラムが一貫して複数の値を返すか複数の値を交換する必要がある場所を見つけた場合、Tupleルートに行く価値があるかもしれませんが、そうでない場合は、古典的な方法で行う方が簡単な場合があります。

一般的に言えば、Boostがまだ誰もインストールされているわけではなく、私は確かにそれをダウンロードし、タプル機能のためだけに動作するようにインクルードディレクトリを設定するという面倒なことはしません。 Boostを既に使用している人は、Boostを使用していないユーザーよりもプログラムでTupleの使用を見つける可能性が高く、他の言語(Pythonが思い浮かぶ)からの移民は、単にtupleの欠如に腹を立てる可能性が高いと思いますTupleサポートを追加する方法を探るよりもC++で。

3
Zac

データストアとして_std::Tuple_にはstructと配列の両方の最悪の特性があります。すべてのアクセスはn番目の位置に基づいていますが、Tupleループを使用してforを反復処理することはできません。

したがって、Tupleの要素が概念的に配列である場合、配列を使用し、要素が概念的に配列でない場合、構造体(名前付き要素を含む)の保守性が向上します。 (_a.lastname_はstd::get<1>(a)よりも説明的です)。

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

1
doron

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

0
dirkgently