C ++, salin set ke vektor


146

Saya perlu menyalin std::setke 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

Dimana masalahnya?


5
ada juga assign()fungsi:output.assign(input.begin(), input.end());
Gene Bushuyev

vektor Anda kosong. Ada banyak cara untuk memperbaiki itu meskipun orang menunjukkan di bawah ini.
AJG85

@ Gen: assign () ingin memesan () jumlah penyimpanan yang diperlukan sebelumnya. Ini akan menggunakan input iterator untuk menentukan berapa yang dibutuhkan, kecuali iteratornya benar-benar InputIterator, dalam hal ini akan melewati reservasi dan menghasilkan realokasi pada setiap push_back (). Di ujung lain dari spektrum, BiderectionalIterators akan memungkinkannya untuk mengurangi akhir saja. std :: set iterators, bagaimanapun, tidak (mereka adalah ForwardIterator), dan itu sangat disayangkan: dalam hal ini, assign () hanya akan berjalan di seluruh set untuk menentukan ukurannya - kinerja buruk pada set besar.
Sergey Shevchenko

Jawaban:


213

Anda perlu menggunakan back_inserter:

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

std::copytidak menambahkan elemen ke wadah yang Anda masukkan: tidak bisa; hanya memiliki iterator ke dalam wadah. Karena itu, jika Anda meneruskan iterator keluaran langsung ke std::copy, Anda harus memastikan itu menunjuk ke kisaran yang setidaknya cukup besar untuk menahan rentang input.

std::back_insertermembuat iterator keluaran yang memanggil push_backwadah untuk setiap elemen, sehingga setiap elemen dimasukkan ke dalam wadah. Atau, Anda bisa membuat sejumlah elemen dalam std::vectoruntuk menahan rentang yang disalin:

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

Atau, Anda bisa menggunakan std::vectorkonstruktor rentang:

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

3
Hai James, alih-alih baris std :: copy Anda (blok kode pertama dalam jawaban Anda), tidak bisakah saya melakukannya output.insert(output.end(), input.begin(), input.end());?
user2015453

atau hanya menggunakan versi cbegin dan cend: output.insert(output.cend(), input.cbegin(), input.cend());Bagaimana menurut Anda? Terima kasih.
user2015453

2
Haruskah saya output.reserve (input.size ()); sendiri atau bisakah saya berharap bahwa beberapa kompiler melakukannya untuk saya?
jimifiki

@jimifiki, jangan harap aku takut.
Alexis Wilke

Inisialisasi vektor pertama Anda salah. Anda membuat array input,size()entri kosong dan kemudian menambahkan append setelah itu. Saya pikir Anda bermaksud menggunakannya std::vector<double> output; output.reserve(input.size()); std::copy(...);.
Alexis Wilke

121

Cukup gunakan konstruktor untuk vektor yang mengambil iterator:

std::set<T> s;

//...

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

Asumsikan Anda hanya ingin konten s di v, dan tidak ada di v sebelum menyalin data ke dalamnya.


42

inilah alternatif lain menggunakan vector::assign:

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

24

Anda belum memesan ruang yang cukup di objek vektor untuk menyimpan konten set Anda.

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

1
Ini tidak layak -1. Secara khusus, ini memungkinkan vektor untuk hanya melakukan satu alokasi (karena tidak dapat menentukan jarak set iterator di O (1)), dan, jika tidak didefinisikan untuk vektor untuk nol setiap elemen ketika dibangun, ini bisa bermanfaat untuk membiarkan salinan mendidih menjadi memcpy. Yang terakhir masih bisa bermanfaat jika angka implementasi loop di ctor vektor dapat dihapus. Tentu saja, yang pertama juga dapat dicapai dengan cadangan.
Fred Nurk

Saya tidak tahu Biarkan saya membantu Anda dengan itu.
wilhelmtell

Saya memberi Anda -1, tapi itu adalah bagian dari saya. Buat edit kecil agar saya dapat membatalkan suara saya, dan saya akan memberi Anda +1: ini sebenarnya solusi yang sangat bersih karena properti gagal-pertama.
Fred Foo

Saya baru saja mengetahui bahwa jika saya mengedit sendiri jawabannya, saya dapat melakukan upvote. Apakah itu, memberi Anda +1 untuk alokasi memori gagal-pertama. Maaf!
Fred Foo

3

Saya pikir cara yang paling efisien adalah dengan melakukan pra-alokasi dan kemudian menggunakan elemen:

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

Dengan begitu kita hanya akan memanggil copy constructor untuk setiap elemen sebagai lawan memanggil constructor default terlebih dahulu dan kemudian menyalin operator penugasan untuk solusi lain yang tercantum di atas. Penjelasan lebih lanjut di bawah ini.

  1. back_inserter dapat digunakan tetapi akan memanggil push_back () pada vektor ( https://en.cppreference.com/w/cpp/iterator/back_insert_iterator ). emplace_back () lebih efisien karena ia menghindari membuat sementara ketika menggunakan push_back () . Ini bukan masalah dengan tipe yang dibangun secara sepele tetapi akan menjadi implikasi kinerja untuk tipe yang tidak dibangun dengan sepele (misalnya std :: string).

  2. Kita perlu menghindari membangun vektor dengan argumen ukuran yang menyebabkan semua elemen dibangun secara default (tanpa biaya). Seperti dengan solusi menggunakan std :: copy () , misalnya.

  3. Dan, akhirnya, metode vector :: assign () atau konstruktor yang mengambil rentang iterator bukanlah pilihan yang baik karena mereka akan memanggil std :: distance () (untuk mengetahui jumlah elemen) pada set iterators. Ini akan menyebabkan iterasi tambahan yang tidak diinginkan melalui semua elemen set karena set adalah struktur data Binary Search Tree dan tidak menerapkan iterator akses acak.

Semoga itu bisa membantu.


tolong tambahkan referensi ke otoritas mengapa ini cepat dan sesuatu seperti mengapa a back_insertertidak perlu digunakan
Tarick Welling

Menambahkan lebih banyak klarifikasi dalam jawabannya.
dshvets1

1

std::copytidak dapat digunakan untuk memasukkan ke dalam wadah kosong. Untuk melakukan itu, Anda perlu menggunakan insert_iterator seperti:

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
Gagal ini pertama kali vektor merealokasi: iterator dari output.begin () menjadi tidak valid.
Fred Nurk
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.