std :: back_inserter untuk std :: set?

98

Saya rasa ini adalah pertanyaan sederhana. Saya perlu melakukan sesuatu seperti ini:

std::set<int> s1, s2;
s1 = getAnExcitingSet();
std::transform(s1.begin(), s1.end(), std::back_inserter(s2), ExcitingUnaryFunctor());

Tentu saja, std::back_insertertidak berhasil karena tidak ada push_back. std::inserterjuga membutuhkan iterator? Saya belum pernah menggunakan std::inserterjadi saya tidak yakin harus berbuat apa.

Apakah ada yang punya ide?


Tentu saja, pilihan saya yang lain adalah menggunakan vektor untuk s2, lalu menyortirnya nanti. Mungkin itu lebih baik?

rlbond.dll
sumber

Jawaban:

143

settidak memiliki push_backkarena posisi elemen ditentukan oleh pembanding himpunan. Gunakan std::inserterdan berikan .begin():

std::set<int> s1, s2;
s1 = getAnExcitingSet();
transform(s1.begin(), s1.end(), 
          std::inserter(s2, s2.begin()), ExcitingUnaryFunctor());

Iterator sisipkan kemudian akan memanggil di s2.insert(s2.begin(), x)mana xnilai yang diteruskan ke iterator saat dituliskan padanya. Set menggunakan iterator sebagai petunjuk di mana harus menyisipkan. Anda juga bisa menggunakan s2.end().

Johannes Schaub - litb
sumber
2
Karena inserter(vec, vec.end())berfungsi untuk vektor, juga, mengapa ada orang yang menggunakan back_inserter?
NHDaly
7
@NHDaly: karena back_inserter lebih cepat
marton78
@ marton78 Tetapi bukankah seharusnya itu hanya lebih cepat dengan selisih yang sangat kecil, jika sama sekali? Memanggil insertalih-alih push_backpada vektor harus hampir sama (O (1)) ketika tidak ada elemen yang harus dipindahkan.
Felix Dombek
3
@FelixDombek Anda benar, itu tidak akan jauh lebih lambat. v.insert(x, v.end())akan memiliki cabang tambahan di awal (karena itu memindahkan n elemen, tetapi di sini n adalah nol). Namun, menggunakan inserter1) mengkomunikasikan maksud yang berbeda dari menggunakan push_back2) tidak biasa dan membuat pembaca berhenti dan berpikir 3) adalah pesimisasi dini.
marton78
0

Pada tahun 2016 ada usulan untuk memiliki " inserteriterator argumen tunggal ". https://isocpp.org/files/papers/p0471r0.html . Saya tidak dapat menemukan apakah proposalnya maju. Saya pikir itu masuk akal.

Untuk saat ini Anda dapat memiliki perilaku ini yang mendefinisikan fungsi pembuat:

template<class Container>
auto sinserter(Container& c){
    using std::end;
    return std::inserter(c, end(c));
}

Digunakan sebagai:

std::transform(begin(my_vec), end(my_vec), sinserter(my_set), [](auto& e){return e.member;});
alfC
sumber
Apakah ini dimaksudkan untuk bekerja pada semua kontainer standar? Ini tidak bekerja pada std :: forward_list (kesalahan kompilernya adalah "forward_list tidak memiliki anggota bernama 'insert'", di dalam instantiation of insert_iterator::operator=). Haruskah itu?
Don Hatch
@DonHatch, apa pun yang memiliki insert(dan end). sepertinya itu forward_listtidak memiliki insertoperasi di tempat pertama, hanya insert_after. Dan bahkan jika yang diubah itu tidak dapat dimasukkan setelah akhir, saya pikir. Tidak bisakah Anda menggunakan sebagai std::listgantinya?
alfC
Tentu, saya pribadi tidak membutuhkan std :: forward_list. Tapi saya tertarik dengan istilah umum "bagaimana cara menyalin satu wadah ke wadah lain?" untuk semua pasang wadah yang masuk akal. Minat saya saat ini adalah untuk melatih pengalokasi kontainer umum seperti short_alloc @HowardHinnant.
Don Hatch
@DonHatch, masalahnya adalah ada banyak varian untuk pertanyaan itu. ("" bagaimana cara menyalin satu wadah ke wadah lainnya? "). Misalnya, apakah Anda ingin mempertahankan nilai asli, meminimalkan alokasi, dll. Untuk kasus paling sederhana, jawaban terbaik menurut saya adalah menggunakan konstruktor wadah yang membutuhkan dua iterator (sebagian besar wadah dapat mengambilnya) NewContaner new_container(old_other_container.begin(), old_other_container.end()).
alfC
1
Ya, std :: set bukanlah SequentialContainer, dan tidak masalah :-) existing_list = std::list(c.begin(), c.end(), existing_list.get_allocator()) Sangat bagus, saya rasa itulah jawaban saya. Bersulang!
Don Hatch