(一部の)アイテムを1つのstd :: mapから別のstd :: mapに効率的に移動する方法は?


8

2つのstd::map<>オブジェクトがaありb、いくつかの述語に基づいて、1つのマップから別のマップにいくつかの要素(ノード)を移動(extract+ insert)したいと思いpます。

for (auto i = a.begin(); i != a.end(); ++i)
    if (p(*i))
        b.insert(a.extract(i))

このコードはclangでsegfaultします。問題は、iノードがaから抽出された後のの増加であると思います。

ポストインクリメントを使用してこれを修正する正しい/唯一の方法はありますか?例:

for (auto i = a.begin(); i != a.end();)
    if (p(*i))
        b.insert(a.extract(i++))
    else
        ++i;

編集:現在の設定ではこれを再現できないため、「gccで動作する理由」に関する部分を削除しました。ある時点でそれが以前は使用されていたと確信していますが、gcc 9.2.1では(segfaultの代わりに)デッドロックが発生します。いずれにしても、後の増分extract()は機能しません。


2
関連または
重複

3
@Eljay私の考えでは、C ++ 17 の新しいマップ「ノードハンドル」スプライシングAPIは、独自の質問を正当化するために十分に専門化されています。これが複製として閉じられていないことを願っています。
NicholasM

反復中のstd :: setからの要素削除の重複の可能性がありますstd::setstd::mapは非常に似ており、私の知る限り、とextract同じ無効化の影響がありeraseます。
フランソワアンドリュー

どのバージョンのclangおよびgccを使用しましたか?私にとって、clang 8.0とgcc 7.4を使用すると、どちらもsegfaultが発生します。
バラージュKovacsics

このコードがどのコンパイラでも機能することに驚いています。抽出による無効化を処理していない
Iman Kianrostami

回答:


6

問題は、ノードがaから抽出された後のiの増加にあると思います。

確かに。抽出は、抽出された要素に対する反復子を無効にしますi。これは、そのような反復子です。無効な反復子による増分または間接化の動作は定義されていません。

なぜこれはgccでは機能しているように見えますが、clangでは機能しないのですか?

プログラムの動作が定義されていないためです。

これをポストインクリメントで修正する正しい/唯一の方法はありますか?

それは、この問題を解決する正しい方法。特に悪い方法ではありません。増分を繰り返さない場合は、変数を使用する方法があります。

for (auto i = a.begin(); i != a.end();) {
    auto current = i++;
    if (p(*current)) {
        // moving is probably unnecessary
        b.insert(a.extract(std::move(current)));
    }
}

(合理的には)イテレータの状態をコピーする方がノードをコピーするよりも安価であると想定すると、これは良い方法です。
Spencer

@Spencerがイテレータをコピーすることは、通常は簡単です。しかし念のために手を加えました。
eerorika

@Spencer currentは、移動状態のままになります。それ以降は使用されないため、その状態は問題ではありません。
eerorika

@eeroikaありがとう、私はあなたのコードをもう少し詳しく見て、それを実現しました。
Spencer

2つのicrementよりもローカル変数の方が好きですが、マイナーな改善を提案しますcurrent。c++ 17s if (auto current = ++i; p(*current))構文を使用してスコープを制限します。
axxel
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.