16

次のコードを見てください。

#include <utility>
#include <map>

// non-copyable but movable
struct non_copyable {
    non_copyable() = default;

    non_copyable(non_copyable&&) = default;
    non_copyable& operator=(non_copyable&&) = default;

    // you shall not copy
    non_copyable(const non_copyable&) = delete;
    non_copyable& operator=(const non_copyable&) = delete;
};

int main() {
    std::map<int, non_copyable> map;
    //map.insert({ 1, non_copyable() });  < FAILS
    map.insert(std::make_pair(1, non_copyable()));
    // ^ same and works
}

g++ 4.7 でマークされた行のコメントを外すと、このスニペットのコンパイルが失敗します。生成されたエラーは、non_copyableコピーできないことを示していますが、移動されることを期待していました。

std::pair均一な初期化を使用して構築されたものを挿入すると失敗するのに、を使用して構築されたものを挿入できないのはなぜstd::make_pairですか? 両方とも、マップに正常に移動できる右辺値を生成するはずではありませんか?

4

2 に答える 2

22

[これは完全な書き直しです。私の以前の答えは問題とは何の関係もありませんでした。]

mapは2つの関連するinsertオーバーロードがあります。

  • insert(const value_type& value)、 と

  • <template typename P> insert(P&& value)

単純なリスト初期化子を使用する場合、map.insert({1, non_copyable()});考えられるすべての過負荷が考慮されます。ただし、最初の1つ(取っているものconst value_type&)だけが見つかります。もう1つは意味がないためです(ペアを作成するつもりだったと魔法のように推測する方法はありません)。もちろん、要素はコピーできないため、最初のオーバーロードは機能しません。

すでに説明したように、を使用してペアを明示的に作成するかmake_pair、値型に明示的に名前を付けることで、2番目のオーバーロードを機能させることができます。

typedef std::map<int, non_copyable> map_type;

map_type m;
m.insert(map_type::value_type({1, non_copyable()}));

これで、list-initializerはコンストラクターを探すことを認識しmap_type::value_type、関連する可動コンストラクターを見つけます。その結果P&&、関数の-overloadにバインドする右辺値のペアが作成されinsertます。

(もう1つのオプションは、 andを使用emplace()することですが、これにより、より詳細になります。)piecewise_constructforward_as_tuple

ここでの教訓は、リスト初期化子が実行可能なオーバーロードを探すことだと思いますが、何を探すべきかを知っている必要があります。

于 2013-02-17T00:05:14.273 に答える