web-dev-qa-db-ja.com

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

私はC++プログラマーであり、経験は限られています。

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

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

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

具体的には、単純なstructの代わりにpairを使用するとオーバーヘッドが発生しますか?

26
Marco Stramezzi

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

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

33
risingDarkness

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(またはタプル)の不適切な使用例は次のようになります。

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を使用します)。コード内のどこかに関係が存在する場合は、構造体を使用する必要があります。

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

3
Damian Jarek