2013-02-11 1 views
25

問題にオブジェクトを移動すると、巨大な物体が、私は動きを保証することができますどのようにマップ マップこれに

Huge huge1(some,args); 
Huge huge2(some,args); 

std::map<int,Huge> map1; 
std::map<Huge,int> map2; 

map1.insert({0,huge1}); 
map2.insert({huge2,0}); 

にコピーされることがありますか?これはうまくいくのでしょうか、それともそれ以上はありますか?

std::pair<iterator,bool> insert(value_type&&);

R値コンストラクタを呼び出すこの過負荷に特異的に結合する任意の式:

map1.insert({0,std::move(huge1)}); 
map2.insert({std::move(huge2),0}); 
+1

私はこの数日前に正確に尋ねました:http://stackoverflow.com/questions/14581414/insert-map-entry-by-r-value-moving-of-mapped-type – Chowlett

+0

全く同じことではありませんが、 @Chowlett。 – Yakk

+0

@Yakk - ...キータイプは移動可能なので、コピーを確実にする必要があります。あるいは私は何か他のものを逃していますか – Chowlett

答えて

37

std::map::insertはR値のために過負荷を有しています。

template<class U1, class U2> 
pair(U1&& x, U2&& y); 

は、あなたがkey_typemapped_typeためのR-値コンストラクタは、両方のpairオブジェクトの作成中に、呼び出されることが保証され、かつ:std::map<K,V>::value_typestd::pair<const key_type, mapped_type>あり、そしてstd::pairはR-値を取るコンストラクタを持っているので、

map1.insert(std::make_pair(0, Huge()); 

OR

map1.insert(std::make_pair(0, std::move(huge1)); 
次のような限り、あなたはR-値を作成する式を使用してペアを挿入すると、マップの挿入、中もちろん

、このすべては、適切なR値コンストラクタ持つHugeに依存しています。最後に

Huge(Huge&& h) 
{ 
    ... 
} 


をあなたは、単に要素として新しいHugeオブジェクトを構築したい場合、あなたはまた、std::map::emplaceを使用することができます地図上に表示されます。

+1

また、ブレースイニシャライザで使用する例も動作すると付け加えます。また、ペアコンストラクタは実際には汎用引数コンストラクタであり、引数のすべて/いずれか/すべてがr値であり、それでも選択されます。 r値のコンストラクタを呼び出すと、両方の引数がr値でなければならないと考える人々を混乱させる可能性がありますが、これは真ではありません。 – mmocny

14

これは可能です({0,std::move(huge1)}部分)。

map1.emplace(std::piecewise_construct, 0, std::forward_as_tuple(some, args)); 
map2.emplace(std::piecewise_construct, std::forward_as_tuple(some, args), 0); 

それとも、あなたの関数は、オブジェクトを指定された場合、あなたはまだemplaceを使用することができます:

map1.emplace(0, std::move(huge1)); 
map2.emplace(std::move(huge1), 0); 
しかし、あなたはまた、次のように(あなたが関数内でオブジェクトを構築していると仮定して)仲介をスキップすることができ
6

コピーと移動の両方を避ける代わりに、std::map::emplace()を使用することもできます。リンク先の参照ページから:

新しい要素をコンテナに挿入します。 要素はインプレースで構築されます。つまり、コピー操作または移動操作は実行されません。、関数に供給STDが前方(引数)::と転送と全く同じ引数と呼ばれる要素のタイプ(VALUE_TYPE、つまり、スタンダード::ペア)のコンストラクタ....

2

上記と同様に、std::unique_ptr<>のコピーコンストラクタがないことにも頼ることができますが、これによってインターフェイスが少し変更されます。予想される出力を生成し

#include <iostream> 
#include <map> 
#include <memory> 

class Huge { 
public: 
    Huge(int i) : x{i} {} 
    int x; 
}; 

using HugePtrT = std::unique_ptr<Huge>; 
using MyMapT = std::map<int, HugePtrT>; 


int 
main() { 
    MyMapT myMap; 
    myMap[42].reset(new Huge{1}); 
    std::cout << myMap[42]->x << std::endl; 
    myMap[43] = std::move(myMap[42]); 
    if (myMap[42]) 
    std::cout << "42: " << myMap[42]->x << std::endl; 
    if (myMap[43]) 
    std::cout << "43: " << myMap[43]->x << std::endl; 
} 

:あなたはstd::move()呼び出しを省略した場合

1 
43: 1 

、プログラムはコンパイルに失敗します。同様に、.reset()を使用してポインタを割り当てることができます。

これは、R値コンストラクタを持たないクラスで動作し、非常に軽量で、メモリの所有権が明確に定義され、boost::optional<>のようなセマンティクスを提供するという利点があります。引数は、std::unique_ptrがR値コンストラクタ経由で移動されたオブジェクトよりも軽いことができます。なぜなら、R値Moveオブジェクトは割り当てを必要とするからです(ただし、オブジェクトの腸が動いたとしても、戻り値最適化またはコピーエリートをサポートしています)。

std::unique_ptr<>の理由は、std::unique_ptr<>にはコピーコンストラクタがないため、移動コンストラクタしかありません。

関連する問題