構造体とstd :: pairの使用にはどのような違いがありますか?


26

私は経験が少ないC ++プログラマです。

を使用しSTL mapていくつかのデータを保存および操作したい場合、これらの2つのデータ構造アプローチの間に(パフォーマンスにも)意味のある違いがあるかどうかを知りたいと思います。

Choice 1:
    map<int, pair<string, bool> >

Choice 2:
    struct Ente {
        string name;
        bool flag;
    }
    map<int, Ente>

具体的にはstruct、シンプルではなくを使用してオーバーヘッドがありますpairか?


18
A std::pair 構造体です。
カレス

3
@gnat:このような一般的な質問は、特にこのような特定の質問に適しただまされやすいターゲットではありません。
ロバートハーベイ

18
@Caleth- std::pairテンプレートです。std::pair<string, bool>構造体です。
ピートベッカー

4
pairセマンティクスがまったくありません。あなたのコードを読んでいる人(将来あなたも含む)e.firstは、明示的に指摘しない限り、それが何かの名前であることを知りません。私はその中で固く信じていますpairと非常に貧しいと怠惰加えたstd、そしてそれは考えられていたとき、誰もが、いつの日か、誰もがこれを使用しようとしている」と思っていないことをすべての二つのことで、誰もが何を誰のコード手段を知っているんだろう「。
ジェイソンC

2
@スノーマンああ、間違いなく。それでも、mapイテレータは有効な例外ではないというのはあまりにも悪いことです。(「最初」=キーと「2番目」=値...本当に、std本当に?)
ジェイソンC

回答:


33

選択肢1は、小さな「一度だけ使用する」ものには問題ありません。基本的にstd::pairはまだ構造体です。このコメントの選択肢1で述べたように、うさぎの穴のどこかに非常にugいコードが導かれthing.second->first.second->second、誰もそれを解読したくありません。

選択肢2は、マップ内の物の意味が何であるかを読みやすくするため、他のすべての場合に適しています。また、データを変更する場合(たとえば、Enteが突然別のフラグを必要とする場合)には、より柔軟です。ここではパフォーマンスは問題になりません。


15

パフォーマンス

場合によります。

特定のケースでは、2つが同様にメモリに配置されるため、パフォーマンスの違いはありません。

非常に特殊なケース(空の構造体をデータメンバーの1つとして使用している場合)std::pair<>では、Empty Base Optimization(EBO)を潜在的に使用し、構造体と同等のサイズよりも小さい可能性があります。通常、サイズが小さいほどパフォーマンスが高くなります。

struct Empty {};
struct Thing { std::string name; Empty e; };

int main() {
    std::cout << sizeof(std::string) << "\n";
    std::cout << sizeof(std::tuple<std::string, Empty>) << "\n";
    std::cout << sizeof(std::pair<std::string, Empty>) << "\n";
    std::cout << sizeof(Thing) << "\n";
}

版画:32、32、40、40にideone

注:通常のペアで実際にEBOトリックを使用する実装は知りませんが、通常はタプルで使用されます。


読みやすさ

ただし、マイクロ最適化とは別に、名前付き構造はより人間工学的です。

私が意味するのは、ほとんどわかりmap[k].firstにくいですが、それほど悪くはありませんget<0>(map[k])。これとは対照的に、map[k].name私たちが読んでいるものをすぐに示します。

タイプを相互に変換できる場合、それらのタイプを不注意に交換することが実際の関心事になるため、より重要です。

また、構造入力と名義入力について読むこともできます。Ente唯一の期待のものによって操作することができ、特定のタイプでEnte、上で動作することができます何でもstd::pair<std::string, bool>それらを操作することができますが...場合でも、std::stringまたはboolので、彼らは、期待するものが含まれていませんstd::pairまったく持っていないセマンティクス、それに関連します。


メンテナンス

メンテナンスに関してpairは、最悪です。フィールドを追加することはできません。

tupleその点では、新しいフィールドを追加する限り、既存のすべてのフィールドに同じインデックスが引き続きアクセスします。これは以前と同じように不可解ですが、少なくともそれらを更新する必要はありません。

struct明確な勝者です。好きな場所にフィールドを追加できます。


結論として:

  • pair 両方の世界で最悪です
  • tuple 非常に特殊な場合(空のタイプ)にわずかなエッジがある場合があります。
  • を使用しますstruct

注:ゲッターを使用する場合、クライアントがそれについて知る必要なく、空の基本トリックを自分で使用できますstruct Thing: Empty { std::string name; }。これが、カプセル化が次のトピックである理由です。


3
標準に従っている場合、ペアにEBOを使用することはできません。ペアの要素はに格納されているメンバー firstsecond空のための場所がない、ベースでキックに最適化。
Revolver_Ocelot

2
@Revolver_Ocelot:まあ、EBOを使用するC ++ を書くことはできませんpairが、コンパイラーは組み込み機能を提供できます。ただし、これらはメンバーであることが想定されているため、(たとえば、アドレスを確認するなど)観察可能かもしれません。その場合、準拠していません。
マチューM.17年

1
C ++ 20は[[no_unique_address]]、メンバーのEBOと同等の機能を有効にします。
underscore_d

3

ペアは、関数の戻り値の型として、std :: tieとC ++ 17の構造化バインディングを使用した非構造化割り当てと組み合わせて使用​​すると最も効果的です。std :: tie:を使用する

struct Ente {/*...*/};
std::map<int, Ente> map;
auto inserted_position = map.end();
auto was_inserted = false;
std::tie(inserted_position, was_inserted) = map.emplace(1, Ente{});
if (!was_inserted) {
    //handle insertion error
}

C ++ 17の構造化バインディングの使用:

struct Ente {/*...*/};
std::map<int, Ente> map;
auto [inserted_position, was_inserted] = map.emplace(1, Ente{});
if (!was_inserted) {
    //handle insertion error
}

std :: pair(またはtuple)の使用の悪い例は次のようなものです:

using player_data = std::tuple<std::string, uint64_t, double>;
player_data player{};
/* ... */
auto health = std::get<2>(player);
/* ... */

std :: GET <2>位置インデックスに保存されているもの(player_data)は、読みやすさを覚えておいてください。2.を呼び出すと、コードが何をしているのか、読者のために、それは明らかになったときにそれが明確でないためである重要。これははるかに読みやすいと考えてください:

struct player_data
{
    std::string name;
    uint64_t player_id;
    double current_health;
};
player_data player{};
/* ... */
auto health = player.current_health;
/* ... */

一般に、関数から複数のオブジェクトを返す方法として、std :: pairとstd :: tupleについて考える必要があります。私が使用する(そして他の多くのユーザーも使用している)経験則では、std :: tupleまたはstd :: pairで返されるオブジェクトは、それらを返す関数を呼び出すコンテキスト内でのみ「関連」します。またはそれらを一緒にリンクするデータ構造のコンテキストで(たとえば、std :: mapはストレージタイプにstd :: pairを使用します)。コード内のどこかに関係が存在する場合は、構造体を使用する必要があります。

コアガイドラインの関連セクション:

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