C ++, sao chép được đặt thành vector


146

Tôi cần sao chép std::setvào std::vector:

std::set <double> input;
input.insert(5);
input.insert(6);

std::vector <double> output;
std::copy(input.begin(), input.end(), output.begin()); //Error: Vector iterator not dereferencable

Vấn đề ở đâu?


5
Ngoài ra còn có assign()chức năng:output.assign(input.begin(), input.end());
Gene Bushuyev

vector của bạn là trống rỗng. Có vô số cách để khắc phục mà mặc dù mọi người đang chỉ ra dưới đây.
AJG85

@Gene: gán () muốn dự trữ () số lượng lưu trữ cần thiết trước thời hạn. Nó sẽ sử dụng các trình vòng lặp đầu vào để xác định mức độ cần thiết, trừ khi các trình vòng lặp đó đúng là InputIterator, trong trường hợp đó, nó sẽ bỏ qua việc dự trữ và dẫn đến việc phân bổ lại trên mỗi Push_back (). Ở phía đối diện của quang phổ, BiderectionalIterators sẽ cho phép nó chỉ trừ kết thúc - bắt đầu. Tuy nhiên, các trình lặp của std :: set không phải (chúng là ForwardIterator) và thật không may: trong trường hợp này, gán () sẽ chỉ đi bộ toàn bộ để xác định kích thước của nó - hiệu suất xấu trên các tập lớn.
Serge Shevchenko

Câu trả lời:


213

Bạn cần sử dụng một back_inserter:

std::copy(input.begin(), input.end(), std::back_inserter(output));

std::copykhông thêm các phần tử vào vùng chứa mà bạn đang chèn: không thể; nó chỉ có một iterator vào container. Bởi vì điều này, nếu bạn truyền trực tiếp một trình vòng lặp đầu ra std::copy, bạn phải chắc chắn rằng nó trỏ đến một phạm vi ít nhất đủ lớn để giữ phạm vi đầu vào.

std::back_insertertạo một trình vòng lặp đầu ra gọi push_backtrên một thùng chứa cho mỗi phần tử, vì vậy mỗi phần tử được chèn vào vùng chứa. Ngoài ra, bạn có thể đã tạo đủ số lượng phần tử trong std::vectorphạm vi để giữ phạm vi được sao chép:

std::vector<double> output(input.size());
std::copy(input.begin(), input.end(), output.begin());

Hoặc, bạn có thể sử dụng hàm tạo std::vectorphạm vi:

std::vector<double> output(input.begin(), input.end()); 

3
Xin chào James, thay vì dòng std :: copy của bạn (khối mã đầu tiên trong câu trả lời của bạn), tôi không thể làm output.insert(output.end(), input.begin(), input.end());thay thế?
dùng2015453

hoặc chỉ sử dụng phiên bản cbegin và cend: output.insert(output.cend(), input.cbegin(), input.cend());Bạn nghĩ gì? Cảm ơn.
dùng2015453

2
Tôi có nên output.reserve (input.size ()); một mình hoặc tôi có thể hy vọng rằng một số trình biên dịch làm điều đó cho tôi?
jimifiki

@jimifiki, không hy vọng tôi sợ.
Alexis Wilke

Khởi tạo vector đầu tiên của bạn là không chính xác. Bạn tạo một mảng các input,size()mục trống và sau đó nối thêm các phần phụ sau đó. Tôi nghĩ bạn có nghĩa là để sử dụng std::vector<double> output; output.reserve(input.size()); std::copy(...);.
Alexis Wilke

121

Chỉ cần sử dụng hàm tạo cho vectơ có các vòng lặp:

std::set<T> s;

//...

std::vector v( s.begin(), s.end() );

Giả sử bạn chỉ muốn nội dung của s trong v và không có gì trong v trước khi sao chép dữ liệu vào đó.


42

Đây là một cách khác để sử dụng vector::assign:

theVector.assign(theSet.begin(), theSet.end());

24

Bạn chưa dành đủ không gian trong đối tượng vector của mình để chứa nội dung của tập hợp.

std::vector<double> output(input.size());
std::copy(input.begin(), input.end(), output.begin());

1
Điều này không xứng đáng -1. Cụ thể, điều này cho phép vectơ chỉ thực hiện một phân bổ (vì nó không thể xác định khoảng cách của các bộ lặp trong O (1)) và, nếu nó không được xác định cho vectơ để loại bỏ từng phần tử khi được xây dựng, điều này có thể có giá trị để cho phép các bản sao sôi lên một memcpy. Cái sau vẫn có thể có giá trị nếu việc triển khai chỉ ra vòng lặp trong ctor của vector có thể được gỡ bỏ. Tất nhiên, trước đây cũng có thể đạt được với dự trữ.
Fred Nurk

Tôi không biết. Hãy để tôi giúp bạn điều đó.
wilmustell

Tôi đã cho bạn -1, nhưng đó là một suy nghĩ về phía tôi. Thực hiện một chỉnh sửa nhỏ để tôi có thể hoàn tác phiếu bầu của mình và tôi sẽ cho bạn +1: đây thực sự là một giải pháp rất sạch vì thuộc tính đầu tiên không thành công.
Fred Foo

Tôi chỉ tìm ra rằng nếu tôi tự chỉnh sửa câu trả lời, tôi có thể thực hiện một upvote. Đã làm điều đó, đã cho bạn +1 cho phân bổ bộ nhớ đầu tiên không thành công. Lấy làm tiếc!
Fred Foo

3

Tôi nghĩ rằng cách hiệu quả nhất là preallocate và sau đó là các yếu tố:

template <typename T>
std::vector<T> VectorFromSet(const std::set<T>& from)
{
    std::vector<T> to;
    to.reserve(from.size());

    for (auto const& value : from)
        to.emplace_back(value);

    return to;
}

Bằng cách đó, chúng tôi sẽ chỉ gọi hàm tạo sao chép cho mọi phần tử chứ không phải gọi hàm tạo mặc định trước rồi sao chép toán tử gán cho các giải pháp khác được liệt kê ở trên. Làm rõ hơn dưới đây.

  1. back_inserter có thể được sử dụng nhưng nó sẽ gọi push_back () trên vectơ ( https://en.cppreference.com/w/cpp/iterator/back_insert_iterator ). emplace_back () hiệu quả hơn vì nó tránh tạo tạm thời khi sử dụng push_back () . Nó không phải là một vấn đề với các loại được xây dựng tầm thường nhưng sẽ là một hàm ý hiệu suất cho các loại được xây dựng không tầm thường (ví dụ: std :: string).

  2. Chúng ta cần tránh xây dựng một vectơ với đối số kích thước gây ra tất cả các phần tử được xây dựng mặc định (không có gì). Ví dụ như với giải pháp sử dụng std :: copy () .

  3. Và cuối cùng, phương thức vector :: gán () hoặc hàm tạo lấy phạm vi lặp không phải là lựa chọn tốt vì chúng sẽ gọi std :: distance () (để biết số phần tử) trên bộ lặp. Điều này sẽ gây ra lần lặp bổ sung không mong muốn thông qua tất cả các phần tử được đặt vì tập hợp là cấu trúc dữ liệu Cây tìm kiếm nhị phân và nó không thực hiện các trình lặp truy cập ngẫu nhiên.

Mong rằng sẽ giúp.


vui lòng thêm một tham chiếu đến một cơ quan có thẩm quyền tại sao việc này lại nhanh chóng và đại loại như tại sao back_inserterkhông cần sử dụng
Tarick Welling

Thêm nhiều làm rõ hơn trong câu trả lời.
dshvets1

1

std::copykhông thể được sử dụng để chèn vào một container rỗng. Để làm điều đó, bạn cần sử dụng một insert_iterator như vậy:

std::set<double> input;
input.insert(5);
input.insert(6);

std::vector<double> output;
std::copy(input.begin(), input.end(), inserter(output, output.begin())); 

3
Điều này thất bại lần đầu tiên vector phân bổ lại: iterator từ output.begin () bị vô hiệu.
Fred Nurk
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.