2012-05-23 36 views
41

Hãy xem xét các chương trình sau đó chèn một loạt các yếu tố vào một vector:Có cách nào di chuyển một phạm vi tiêu chuẩn thành một véc tơ không?

vector<string> v1; 
vector<string> v2; 

v1.push_back("one"); 
v1.push_back("two"); 
v1.push_back("three"); 

v2.push_back("four"); 
v2.push_back("five"); 
v2.push_back("six"); 

v1.insert(v1.end(), v2.begin(), v2.end()); 

này một cách hiệu quả các bản sao phạm vi, phân bổ đủ không gian trong vector mục tiêu cho toàn bộ phạm vi sao cho tối đa là một thay đổi kích thước sẽ được yêu cầu . Bây giờ xem xét các chương trình sau đó cố gắng để di chuyển một loạt thành một vector:

vector<string> v1; 
vector<string> v2; 

v1.push_back("one"); 
v1.push_back("two"); 
v1.push_back("three"); 

v2.push_back("four"); 
v2.push_back("five"); 
v2.push_back("six"); 

for_each (v2.begin(), v2.end(), [&v1](string & s) 
{ 
    v1.emplace_back(std::move(s)); 
}); 

này thực hiện một động thái thành công nhưng không được hưởng những lợi ích mà chèn() có liên quan đến preallocating không gian trong vector mục tiêu, vì vậy vectơ có thể được thay đổi kích cỡ nhiều lần trong quá trình hoạt động.

Vì vậy, câu hỏi của tôi là, có một tương đương chèn có thể di chuyển một phạm vi vào một véc tơ không?

+1

Nếu bạn cần preallocate không gian trong vector, sử dụng 'std :: vector :: reserve', và giữ' push_back'/'emplace_back'. – rubenvb

+0

@rubenvb Có, tôi nghĩ rằng đó có thể là câu trả lời, nó chỉ là một sự xấu hổ không có một phương pháp sạch sẽ như 'insert()' là. – Benj

+0

Đó sẽ là một tối ưu hóa tùy chọn, chỉ có thể khi phạm vi được xác định bởi các trình vòng lặp truy cập ngẫu nhiên. Đừng tin vào nó. –

Trả lời

65

Bạn sử dụng một move_iterator với insert:

v1.insert(v1.end(), make_move_iterator(v2.begin()), make_move_iterator(v2.end())); 

Ví dụ trong 24.5.3 là gần như chính xác này.

Bạn sẽ nhận được tối ưu hóa bạn muốn nếu (a) vector::insert sử dụng công cụ chuyển thẻ lặp để phát hiện trình vòng lặp truy cập ngẫu nhiên và tính toán trước kích thước (bạn đã giả định nó trong ví dụ của bạn sao chép) và (b) move_iterator duy trì danh mục vòng lặp của trình vòng lặp nó kết thúc tốt đẹp (theo yêu cầu của tiêu chuẩn).

Trên một điểm tối nghĩa: Tôi chắc chắn rằng vector::insert có thể phát ra từ nguồn (không liên quan ở đây, vì nguồn là cùng loại với đích đến, vì vậy một vị trí giống như bản sao/di chuyển, nhưng sẽ có liên quan đến các ví dụ giống hệt nhau). Tôi chưa tìm thấy một tuyên bố rằng nó được yêu cầu để làm như vậy, tôi đã chỉ suy ra nó từ thực tế là yêu cầu trên cặp lặp i,j được chuyển đến insertTEmplaceConstructible từ *i.

+1

Tuyệt vời, không biết về 'make_move_iterator' – Benj

+1

+1 cho backgrounder – sehe

+4

Hmm, tôi đã phát hiện ra rằng nó cũng có thể sử dụng' make_move_iterator' để biến 'std :: copy_if' thành tương đương với' std :: move_if'. Điều đó rất tiện dụng. – Benj

32
  1. std::move thuật toán với preallocation:

    #include <iterator> 
    #include <algorithm> 
    
    v1.reserve(v1.size() + v2.size()); // optional 
    std::move(v2.begin(), v2.end(), std::back_inserter(v1)); 
    
  2. Sau đây sẽ là linh hoạt hơn nào:

    v1.insert(v1.end(), 
        std::make_move_iterator(v2.begin()), 
        std::make_move_iterator(v2.end())); 
    

    Steve Jessop cung cấp thông tin cơ bản về chính xác những gì nó và có lẽ nó như thế nào vì thế.

+0

Khả năng thay đổi kích thước đích 'vectơ' này chỉ một lần rất rất mỏng. –

+1

Oh gọn gàng, không biết về hình thức này của 'std :: move'.Mặc dù tôi đoán 'back_inserter' vẫn có thể gây ra nhiều thay đổi kích thước. – Benj

+0

@BenVoigt Tại sao? Tôi cho rằng nó phụ thuộc vào việc thực hiện. vector :: dự trữ là bạn của bạn. Hãy để tôi kiểm tra tiêu chuẩn. – sehe

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