2013-02-11 31 views
25

Vấn đề với điều này là các đối tượng khổng lồ sẽ được sao chép vào bản đồDi chuyển một đối tượng vào một bản đồ

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}); 

làm thế nào tôi có thể đảm bảo một động thái? Điều này có hiệu quả hay không?

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

Tôi đã hỏi chính xác điều này một vài ngày trước: http://stackoverflow.com/questions/14581414/insert-map-entry-by-r-value-moving-of-mapped-type – Chowlett

+0

Không hoàn toàn giống nhau, @Chowlett. – Yakk

+0

@Yakk - ... vì loại khóa cũng có sẵn để di chuyển, nơi tôi cần để đảm bảo nó được sao chép? Hay tôi còn thiếu cái gì khác? – Chowlett

Trả lời

37

std::map::insert có tình trạng quá tải cho R-giá trị:

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

bất cứ biểu thức mà liên kết với tình trạng quá tải này sẽ gọi constructor R-giá trị. Kể từ std::map<K,V>::value_typestd::pair<const key_type, mapped_type>, và std::pair có một constructor mà mất R-giá trị:

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

sau đó bạn được đảm bảo rằng nhà thầu Giá trị R cho key_typemapped_type sẽ được gọi, cả trong việc tạo ra các đối tượng pair, và trong chèn bản đồ, miễn là bạn chèn các cặp sử dụng một biểu thức mà tạo ra R-giá trị, chẳng hạn như:

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

HOẶC

map1.insert(std::make_pair(0, std::move(huge1)); 

Tất nhiên, tất cả điều này phụ thuộc vào Huge có một constructor Giá trị R thích hợp:

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


Cuối cùng, bạn cũng có thể sử dụng std::map::emplace nếu bạn chỉ đơn giản là muốn xây dựng một đối tượng mới Huge như một yếu tố trên bản đồ.

+1

Tôi cũng sẽ thêm rằng ví dụ anh ta sử dụng với bộ khởi tạo dấu ngoặc đơn cũng hoạt động. Ngoài ra, hàm tạo cặp thực sự là một hàm tạo tham chiếu phổ quát, trong đó tất cả/hoặc/none của các đối số có thể là giá trị r và nó vẫn sẽ được chọn. Gọi nó là một hàm tạo giá trị r có thể gây nhầm lẫn cho mọi người nghĩ rằng cả hai đối số phải là giá trị r, điều này không đúng. – mmocny

14

Bạn có thể làm điều đó (phần {0,std::move(huge1)}). Nhưng bạn cũng có thể bỏ qua trung gian (giả sử bạn đang xây dựng các đối tượng trong phạm vi chức năng) như thế này:

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

Hoặc, nếu chức năng của bạn được cho các đối tượng, bạn vẫn có thể sử dụng emplace:

map1.emplace(0, std::move(huge1)); 
map2.emplace(std::move(huge1), 0); 
6

Một giải pháp thay thế tránh được việc sao chép và di chuyển sẽ là sử dụng std::map::emplace(). Từ trang tham chiếu được liên kết:

Chèn phần tử mới vào vùng chứa. Phần tử được tạo tại chỗ, tức là không có thao tác sao chép hoặc di chuyển nào được thực hiện. Các nhà xây dựng của các loại nguyên tố (value_type, có nghĩa là, std :: cặp) được gọi với chính xác các đối số tương tự như cung cấp cho các chức năng, chuyển tiếp với std :: phía trước (args) ....

2

Cùng với những điều trên, bạn cũng có thể dựa vào việc thiếu một nhà xây dựng bản sao của std::unique_ptr<>, mặc dù điều này làm thay đổi giao diện một chút.

#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; 
} 

trong đó sản xuất sản lượng dự kiến:

1 
43: 1 

Nếu bạn bỏ qua các std::move() cuộc gọi, chương trình sẽ không biên dịch. Tương tự, bạn có thể sử dụng .reset() để gán con trỏ. Điều này có lợi thế là nó sẽ làm việc trên các lớp không có một hàm tạo R, là trọng lượng rất nhẹ, quyền sở hữu bộ nhớ được xác định rõ ràng, và cung cấp cho bạn một ngữ nghĩa giống như boost::optional<>. Một đối số có thể được thực hiện rằng std::unique_ptr là trọng lượng nhẹ hơn một đối tượng đã được di chuyển thông qua hàm tạo giá trị R vì đối tượng di chuyển R giá trị yêu cầu phân bổ (mặc dù công bằng, tất cả các trình biên dịch C++ 11 mà tôi ' m nhận thức được hỗ trợ Giá trị trả về Tối ưu hóa hoặc sao chép elision), mặc dù ruột của đối tượng được di chuyển.

Lý do std::unique_ptr<> hoạt động như thế này là bởi vì std::unique_ptr<> không có hàm tạo bản sao, nó chỉ có một hàm tạo di chuyển.

Các vấn đề liên quan