[解決済み] C++11でunordered_mapが挿入したものを破壊するのはC++標準化委員会の意図するところですか?
質問
私は、unordered_map::insert()が挿入した変数を破壊するという非常に奇妙なバグを追跡するために、人生の3日間を失ったところです。この非常に明白でない動作は、非常に最近のコンパイラでのみ発生します:私は clang 3.2-3.4 と GCC 4.8 を見つけました。 だけです。 コンパイラであることがわかりました。
以下は、私のメイン コード ベースから、この問題を実証するいくつかの縮小されたコードです。
#include <memory>
#include <unordered_map>
#include <iostream>
int main(void)
{
std::unordered_map<int, std::shared_ptr<int>> map;
auto a(std::make_pair(5, std::make_shared<int>(5)));
std::cout << "a.second is " << a.second.get() << std::endl;
map.insert(a); // Note we are NOT doing insert(std::move(a))
std::cout << "a.second is now " << a.second.get() << std::endl;
return 0;
}
私は、おそらくほとんどのC++プログラマと同様に、出力がこのように見えることを期待しています。
a.second is 0x8c14048
a.second is now 0x8c14048
しかし、clang 3.2-3.4 と GCC 4.8 では、代わりに次のようなものが得られます。
a.second is 0xe03088
a.second is now 0
のunordered_map::insert()のドキュメントをよく見てみると、これは意味がわからないかもしれません。 http://www.cplusplus.com/reference/unordered_map/unordered_map/insert/ にあるunordered_map::insert()のドキュメントをよく調べるまで、意味をなさないかもしれません。
template <class P> pair<iterator,bool> insert ( P&& val );
これは貪欲な普遍的参照移動オーバーロードで、他のオーバーロードのどれにもマッチしないものを消費し、そして を構築します。 を構築して、value_type にします。では、なぜ上記のコードはこのオーバーロードを選択し、おそらくほとんどの人が期待するような unordered_map::value_type オーバーロードを選択しなかったのでしょうか?
答えはあなたの顔をじっと見ています:unordered_map::value_typeはpair< const int, std::shared_ptr> であり、コンパイラは正しくpair<.と考えるでしょう。 int std::shared_ptr> が変換可能でないとコンパイラは正しく判断します。したがって、コンパイラは移動普遍参照のオーバーロードを選択し、それはオリジナルを破壊します。 にもかかわらず プログラマがstd::move()を使っていないにもかかわらず。したがって、挿入による破壊の動作は実際には 正しい であり、古いコンパイラは 不正解 .
なぜ私がこのバグを診断するのに3日間もかかったのか、今ならおそらくおわかりでしょう。unordered_map に挿入される型がソース コード用語で遠く離れた場所で定義された typedef であり、typedef が value_type と同一であるかどうかを確認することは誰にも思いつかなかった、大規模なコード ベースではまったく明らかではありませんでした。
そこで、Stack Overflow への私の質問です。
-
なぜ古いコンパイラは新しいコンパイラのように挿入された変数を破棄しないのでしょうか?つまり、GCC 4.7 でさえこれを行わず、かなり標準に準拠しているのです。
-
コンパイラをアップグレードすると、確かに以前は動作していたコードが突然動作しなくなるので、この問題は広く知られているのでしょうか?
-
C++ 標準委員会はこの動作を意図していたのでしょうか?
-
より良い動作を与えるために unordered_map::insert() をどのように修正することを提案しますか?もしここでサポートがあれば、私はこの動作を WG21 に N ノートとして提出し、より良い動作を実装するよう依頼するつもりなので、これを尋ねます。
どのように解決するのですか?
他の人がコメントで指摘しているように、quot;universal" コンストラクタは実際には、常にその引数から移動することになっているわけではありません。引数が本当に rvalue である場合は移動し、lvalue である場合はコピーすることになっています。
あなたが観察した、常に移動する動作は、libstdc++ のバグであり、質問に対するコメントによると、現在は修正されています。好奇心が強い人のために、私は g++-4.8 のヘッダーを調べました。
bits/stl_map.h
, 598-603行目
template<typename _Pair, typename = typename
std::enable_if<std::is_constructible<value_type,
_Pair&&>::value>::type>
std::pair<iterator, bool>
insert(_Pair&& __x)
{ return _M_t._M_insert_unique(std::forward<_Pair>(__x)); }
bits/unordered_map.h
365行目から370行目
template<typename _Pair, typename = typename
std::enable_if<std::is_constructible<value_type,
_Pair&&>::value>::type>
std::pair<iterator, bool>
insert(_Pair&& __x)
{ return _M_h.insert(std::move(__x)); }
後者は、誤って
std::move
を使うべきところ
std::forward
.
関連
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] [Solved] Error C1083: Cannot open include file: 'stdafx.h'
-
[解決済み】Cygwin Make bash コマンドが見つかりません。
-
[解決済み】「corrupted size vs. prev_size」glibc エラーを理解する。
-
[解決済み】'cout'は型名ではない
-
[解決済み】エラー:strcpyがこのスコープで宣言されていない
-
[解決済み】Visual Studio 2013および2015でC++コンパイラーエラーC2280「削除された関数を参照しようとした」が発生する
-
[解決済み] 非静的データメンバの無効な使用
-
[解決済み】システムが指定されたファイルを見つけられませんでした。
-
[解決済み】指定範囲内の乱数で配列を埋める(C++)
-
[解決済み】警告 - 符号付き整数式と符号なし整数式の比較