C++11 には 8 つの連想コンテナーがあります。 C++17 では、新しい要素をより快適に挿入したり、既存の連想コンテナーをマージしたり、要素が類似している場合はあるコンテナーから別のコンテナーに要素を移動したりできます。しかし、それだけではありません。連想コンテナとシーケンシャル コンテナへのアクセスが統合されました。
詳細に入る前に、まず次の質問に答えさせてください。類似した連想コンテナとはどういう意味ですか? 8 つの連想コンテナがあります。
同様に、それらの要素が同じ構造と同じデータ型を持っていることを意味します。 std::set と std::multiset、std::unordered_set と std::unordered_multiset、std::map と std::multimap、std::unordered_map と std::unordered_multimap の要素は同じ構造を持ちます。
もちろん、これは 8 つの連想コンテナの概要にすぎません。それには2つの理由があります。まず、改善されたインターフェースについて書きたいと思います。 2 番目に、詳細については、私の以前の投稿であるハッシュ テーブルを参照してください。
まったく新しいものへ。
連想コンテナの改善されたインターフェース
徹底的な例を挙げて、改善されたインターフェースをお見せしましょう。
// accociativeContainers.cpp #include <iostream> #include <map> #include <string> #include <utility> using namespace std::literals; // 1 template <typename Cont> void printContainer(const Cont& cont, const std::string& mess){ // 2 std::cout << mess; for (const auto& pa: cont){ std::cout << "(" << pa.first << ": " << pa.second << ") "; } std::cout << std::endl; } int main(){ std::map<int, std::string> ordMap{{1, "a"s}, {2, "b"}}; // 3 ordMap.try_emplace(3, 3, 'C'); ordMap.try_emplace(3, 3, 'c'); printContainer(ordMap, "try_emplace: "); std::cout << std::endl; std::map<int, std::string> ordMap2{{3, std::string(3, 'C')}, // 4 {4, std::string(3, 'D')}}; ordMap2.insert_or_assign(5, std::string(3, 'e')); ordMap2.insert_or_assign(5, std::string(3, 'E')); printContainer(ordMap2, "insert_or_assign: "); // 5 std::cout << std::endl; ordMap.merge(ordMap2); // 6 std::cout<< "ordMap.merge(ordMap2)" << std::endl; printContainer(ordMap, " ordMap: "); printContainer(ordMap2, " ordMap2: "); std::cout << std::endl; std::cout << "extract and insert: " << std::endl; std::multimap<int, std::string> multiMap{{2017, std::string(3, 'F')}}; auto nodeHandle = multiMap.extract(2017); // 7 nodeHandle.key() = 6; ordMap.insert(std::move(nodeHandle)); printContainer(ordMap, " ordMap: "); printContainer(multiMap, " multiMap: "); }
ほとんどの場合、std::map が連想コンテナーの最初の選択肢であるため、この例では std::map を使用しています。連想コンテナーが大きく、パフォーマンスが重要な場合は、std::unordered_map について考えてください。投稿連想コンテナ - 単純なパフォーマンス比較では、いくつかのパフォーマンス数値を示しています。
作業を楽にするために、関数テンプレート printContainer (2) を作成して、連想コンテナーを短いメッセージと共に表示しました。同じ引数が using namespace std::literals 式 (1) にも当てはまります。これで、C++ 文字列に新しい組み込みリテラルを使用できるようになりました。 (3) のキーと値のペア {1, "a"s} で使用されていることがわかります。 "a"s は、C++14 以降で使用できる C++ 文字列リテラルです。 C++ 文字列リテラルを取得するには、文字 s を C 文字列リテラル "a" に追加するだけです。
それでは、プログラムを詳しく説明します。よりよく理解するには、出力を見てください。
これらは、新しい要素を連想コンテナーに追加するための 2 つの新しい方法です:try_emplace と insert_or_assign。 ordMap.try_emplace(3, 3, 'C') (3) 新しい要素を ordMap に追加しようとします。最初の 3 は要素のキーで、次の 3 と 'C' は値のコンストラクタに直接行きます。この場合は std::string です。トライといいます。したがって、キーが既に std::map にある場合、何も起こりません。 ordMap2.insert_or_assign(5, std::string(3, 'e')) (4) は異なる動作をします。最初の呼び出し (4) はキーと値のペア 5、std::string("eee") を挿入し、2 番目の呼び出しは std::string("EEE") をキー 5 に割り当てます。
C++17 では、連想コンテナをマージできます (6)。 ordMap.merge(ordMap2) は、連想コンテナ ordMap2 を ordMap にマージします。正式には、このプロセスは「スプライス」と呼ばれます。これは、キーと値のペアで構成される各ノードが ordMap2 から抽出され、キーが ordMap で使用できない場合に ordMap に挿入されることを意味します。キーがすでに ordMap にある場合、何も起こりません。関連するコピーまたは移動操作はありません。転送されたノードへのすべてのポインタと参照は有効なままです。同様のコンテナ間でノードをマージできます。連想コンテナは、同じ構造と同じデータ型を持つ必要があります。
抽出と挿入が続きます (7)。すでに述べたように、各連想コンテナには新しいサブタイプ、いわゆる node_type があります。あるコンテナを別のコンテナにマージすることで暗黙的に使用しました(6)。 node_type を使用して、キーと値のペアのキーを変更することもできます。こちらをご覧ください。 auto nodeHandle multiMap.extract(2017) は、キー 2017 を持つノードを std::multimap
もちろん、ノードを同じ連想コンテナーに挿入することもできます (A)。そこからノードを抽出するか、キーを変更せずにノードを ordMap に挿入します (B)。ノード (C) の値を変更することもできます。
auto nodeHandle = multiMap.extract(2017); // A nodeHandle.key() = 6; multiMap.insert(std::move(nodeHandle)); auto nodeHandle = multiMap.extract(2017); // B ordMap.insert(std::move(nodeHandle)); auto nodeHandle = multiMap.extract(2017); // C nodeHandle.key() = 6; ordMap.insert(std::move(nodeHandle)); ordMap[6] = std::string("ZZZ");
std::map、std::unordered_map、std::multimap、または std::unordered_multimap などの値を持つ連想コンテナー (A) からノードを抽出すると、nodeHandleMap を呼び出すことができるノード nodeHandleMap が得られます。 。鍵()。ノードの値を変更する nodeHandleMap.value() メソッドはありません。不思議なことに、ノード nodeHandleSet を std::set またはその 3 つの兄弟の 1 つから抽出すると、nodeHandleSet.value() を呼び出してキーを変更できます。
C++17 には、コンテナーにアクセスするための 3 つの新しいグローバル関数が用意されています。
均一なコンテナ アクセス
3 つの新しい関数の名前は、std::size、std::empty、および std::data です。
- std::size:STL コンテナ、C++ 文字列、または C 配列のサイズを返します。
- std::empty:指定された STL コンテナー、C++ 文字列、または C 配列が空かどうかを返します。
- std::data:コンテナの要素を含むメモリ ブロックへのポインタを返します。コンテナーにはメソッド データが必要です。これは、std::vector、std::string、および std::array に当てはまります。
次は?
私は C++17 について約 10 の記事を書きました。カテゴリ C++17 です。したがって、私は終わりました。この 2 年間、マルチスレッドについて多くの記事を書いてきました。これらの投稿は、理論、実践、C++17 および C++20 との同時実行性、およびメモリ モデルに関するものでした。ご想像のとおり、以前の投稿を締めくくるために、いくつかの新しい投稿を考えています。したがって、既存のマルチスレッド化と今後の C++ 標準の並行性について次の投稿を書く予定です。最初に、いくつかの用語を定義する必要があります。そこで、データ競合と競合状態について書きます。ドイツ語では、2 つの異なる現象に対して同じ用語「kritischer Wettlauf」を使用します。それは非常に悪いことです。並行処理では、簡潔な用語が鍵となるためです。