何std::pair
のために、なぜ私はそれを使用するのですか、そしてどのような利点boost::compressed_pair
をもたらしますか?
10 に答える
compressed_pair
スペースを節約するためにいくつかのテンプレートトリックを使用します。C++ では、オブジェクト (小さな o) は、別のオブジェクトと同じアドレスを持つことはできません。
だからあなたが持っていても
struct A { };
A
のサイズは 0 にはなりません。
A a1;
A a2;
&a1 == &a2;
これは許可されていません。
しかし、多くのコンパイラは、いわゆる「空の基本クラスの最適化」を行います。
struct A { };
struct B { int x; };
struct C : public A { int x; };
ここで、 とがゼロにならない場合でも同じサイズで問題ありB
ません。C
sizeof(A)
したがってboost::compressed_pair
、この最適化を利用して、可能であれば、ペアの型が空の場合はどちらか一方から継承します。
したがって、次のstd::pair
ようになります(かなりの量、ctorsなどを省略しました):
template<typename FirstType, typename SecondType>
struct pair {
FirstType first;
SecondType second;
};
つまり、FirstType
またはSecondType
がA
である場合、pair<A, int>
は より大きくなければなりませんsizeof(int)
。
しかし、 を使用するcompressed_pair
と、生成されたコードは次のようになります。
struct compressed_pair<A,int> : private A {
int second_;
A first() { return *this; }
int second() { return second_; }
};
sizeof(int)とcompressed_pair<A,int>
同じ大きさになります。
std::pair
2つの値を1つのオブジェクトとしてグループ化するためのデータ型です。 std::map
キーと値のペアに使用します。
あなたが学んpair
でいる間、あなたはチェックアウトするかもしれませんtuple
。似pair
ていますが、任意の数の値をグループ化するためのものです。 tuple
はTR1の一部であり、多くのコンパイラはすでに標準ライブラリの実装に含まれています。
また、詳細な説明については、 『 The C ++ Standard Library Extensions:A Tutorial and Reference by Pete Becker、ISBN-13:9780321412997 』の第1章「タプル」を確認してください。
関数から 2 つの値を返す必要がある場合があり、そのためだけにクラスを作成するのはやり過ぎです。
そのような場合に std:pair が役立ちます。
boost:compressed_pair は、サイズ 0 のメンバーを最適化できると思います。これは、ライブラリ内の重いテンプレート マシンに最も役立ちます。
タイプを直接制御する場合、それは無関係です。
compress_pair が数バイトを気にするというのは奇妙に聞こえるかもしれません。しかし、compressed_pair をどこで使用できるかを考えると、実際には重要になる可能性があります。たとえば、次のコードを考えてみましょう。
boost::function<void(int)> f(boost::bind(&f, _1));
上記のような場合に、compressed_pair を使用すると、突然大きな影響を与える可能性があります。boost::bind が関数ポインターとプレースホルダー_1
をメンバーとしてそれ自体または a 自体に格納するとどうなるstd::pair
でしょうか? まあ、それは まで膨張する可能性がありsizeof(&f) + sizeof(_1)
ます。関数ポインターが 8 バイト (特にメンバー関数では珍しくありません) で、プレースホルダーが 1 バイト (理由については Logan の回答を参照) であると仮定すると、バインド オブジェクトに 9 バイトが必要になる可能性があります。アラインメントのため、これは通常の 32 ビット システムで最大 12 バイトまで肥大化する可能性があります。
boost::function
実装が小さなオブジェクトの最適化を適用することをお勧めします。つまり、小さなboost::function
ファンクターの場合、オブジェクトに直接埋め込まれた小さなバッファーを使用してファンクターを格納します。より大きなファンクターの場合、メモリを取得するために operator new を使用してヒープを使用する必要があります。ブーストバージョン 1.34前後で、この最適化を採用することが決定されました。非常に優れたパフォーマンス上の利点が得られると考えられたためです。
現在、このような小さなバッファーの妥当な (まだかなり小さい) 制限は 8 バイトです。つまり、非常に単純なバインド オブジェクトは小さなバッファーに収まらず、新しい演算子を格納する必要があります。上記のバインド オブジェクトが を使用する場合compressed_pair
、プレースホルダーは空のオブジェクトにすぎないため、実際にはサイズを 8 バイト (非メンバー関数ポインターの場合は 4 バイト) に減らすことができます。
したがって、ほんの数バイトのために多くの考えを無駄にしているように見えるかもしれませんが、実際にはパフォーマンスに大きな影響を与える可能性があります。
std::pair とは何ですか? なぜ使用するのでしょうか?
それは単純な 2 つの要素のタプルです。Boost.Tupleのようなより洗練されたタイプのタプルを実装するために必要なテンプレートやメタプログラミング手法をコンパイラが広くサポートしていなかった時代に、 STLの最初のバージョンで定義されました。
多くの状況で役立ちます。std::pair
標準の連想コンテナで使用されます。これは範囲の単純な形式として使用できますstd::pair<iterator, iterator>
。そのため、2 つの反復子を別々に使用する代わりに、範囲を表す単一のオブジェクトを受け入れるアルゴリズムを定義できます。(これは、多くの状況で便利な代替手段です。)
std::pair は、STL の他のいくつかのコンテナー クラスに役立ちます。
例えば:
std::map<>
std::multimap<>
どちらもキーと値の std::pair を格納します。
マップとマルチマップを使用する場合、ペアへのポインターを使用して要素にアクセスすることがよくあります。
値のペアを格納するための標準クラスです。のようないくつかの標準関数によって返されます/使用されstd::map::insert
ます。
boost::compressed_pair
より効率的であると主張しています:ここを参照してください
追加情報: boost::compressed_pair は、ペアの型の 1 つが空の構造体である場合に役立ちます。これは、ペアの型が他の型からプログラムによって推論される場合に、テンプレートのメタプログラミングでよく使用されます。最後に、通常、何らかの形の「空の構造体」があります。
重いテンプレートメタプログラミングに興味がない限り、「通常の」使用には std::pair をお勧めします。
これは、内部に 2 つの変数がある構造に他なりません。
関数の戻り値に std::pair を使用するのは実際には嫌いです。コードの読者は、.first とは何か、.second とは何かを知っている必要があります。
私が時々使用する妥協案は、参照に明確な名前を付けながら、.first と .second への定数参照をすぐに作成することです。
パラメータや戻り値など、常に一緒に渡す 2 つの情報がある場合があります。もちろん、独自のオブジェクトを作成することもできますが、それが 2 つの小さなプリミティブなどである場合は、ペアで問題ないように見えることがあります。