std :: back_inserter สำหรับ std :: set?


98

ฉันเดาว่านี่เป็นคำถามง่ายๆ ฉันต้องทำสิ่งนี้:

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

แน่นอนไม่ทำงานเนื่องจากไม่มี std::back_inserter ยังต้องการตัววนซ้ำ? ฉันไม่ได้ใช้ดังนั้นฉันไม่แน่ใจว่าจะทำอย่างไรpush_backstd::inserterstd::inserter

ใครมีความคิด?


แน่นอนตัวเลือกอื่นของฉันคือใช้เวกเตอร์สำหรับs2แล้วเรียงลำดับในภายหลัง อาจจะดีกว่านี้?

คำตอบ:


143

setไม่มีpush_backเนื่องจากตำแหน่งขององค์ประกอบถูกกำหนดโดยตัวเปรียบเทียบของชุด ใช้std::inserterและผ่านมัน.begin():

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

ใส่แล้วจะเรียก iterator s2.insert(s2.begin(), x)ที่xเป็นค่าที่ส่งไป iterator เมื่อเขียนไปมัน ชุดนี้ใช้ตัววนซ้ำเพื่อบอกตำแหน่งที่จะแทรก s2.end()คุณสามารถใช้เป็นดี


2
เนื่องจากinserter(vec, vec.end())ใช้กับเวกเตอร์ได้เช่นกันทำไมใคร ๆ ถึงใช้ back_inserter ตั้งแต่แรก?
NHDaly

7
@NHDaly: เพราะ back_inserter เร็วกว่า
marton78

@ marton78 แต่มันไม่ควรเร็วกว่าโดยอัตรากำไรที่น้อยมากถ้าเป็นอย่างนั้น? การเรียกinsertแทนpush_backเวกเตอร์ควรมีค่าใกล้เคียงกัน (O (1)) เมื่อไม่ต้องย้ายองค์ประกอบ
Felix Dombek

3
@FelixDombek คุณพูดถูกมันจะไม่ช้าไปกว่านี้ v.insert(x, v.end())จะมีสาขาเพิ่มเติมที่จุดเริ่มต้น (เนื่องจากมีการย้ายองค์ประกอบ n แต่ที่นี่ n เป็นศูนย์) อย่างไรก็ตามการใช้inserter1) สื่อถึงเจตนาที่แตกต่างจากการใช้push_back2) ผิดปกติและทำให้ผู้อ่านหยุดและคิด 3) เป็นการมองในแง่ร้ายก่อนวัยอันควร
marton78

0

ในปี 2559 มีข้อเสนอให้มี "ตัวทำinserterซ้ำอาร์กิวเมนต์เดียว" https://isocpp.org/files/papers/p0471r0.html ฉันไม่พบว่าเป็นข้อเสนอขั้นสูงหรือไม่ ผมว่าเข้าท่าดี

ในตอนนี้คุณสามารถมีพฤติกรรมนี้ที่กำหนดฟังก์ชันเครื่องชง:

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

ใช้เป็น:

std::transform(begin(my_vec), end(my_vec), sinserter(my_set), [](auto& e){return e.member;});

สิ่งนี้มีไว้เพื่อใช้กับคอนเทนเนอร์มาตรฐานทั้งหมดหรือไม่ ไม่ทำงานบน std :: forward_list (ข้อผิดพลาดของคอมไพลเลอร์คือ "forward_list ไม่มีสมาชิกชื่อ" แทรก "ภายในอินสแตนซ์ของinsert_iterator::operator=) ควรเป็น?
Don Hatch

@ DonHatch อะไรก็ได้ที่มีinsert(และend). มันก็ดูเหมือนว่าforward_listไม่ได้มีการดำเนินการในครั้งแรกเท่านั้นinsert insert_afterและแม้ว่าจะมีการเปลี่ยนแปลง แต่ก็ไม่สามารถแทรกได้หลังจากสิ้นสุดฉันคิดว่า ใช้ a std::listแทนไม่ได้เหรอ?
alfC

แน่นอนว่าโดยส่วนตัวแล้วฉันไม่ต้องการ std :: forward_list แต่ฉันสนใจผลิตภัณฑ์ทั่วไป "ฉันจะคัดลอกคอนเทนเนอร์หนึ่งไปยังอีกคอนเทนเนอร์หนึ่งได้อย่างไร" สำหรับคู่ของคอนเทนเนอร์ทั้งหมดที่เหมาะสม ความสนใจปัจจุบันของฉันคือการใช้ตัวจัดสรรคอนเทนเนอร์ทั่วไปเช่น short_alloc ของ @HowardHinnant
Don Hatch

@ DonHatch สิ่งนี้ก็คือมีหลายรูปแบบสำหรับคำถามนั้น ("" ฉันจะคัดลอกคอนเทนเนอร์หนึ่งไปยังอีกคอนเทนเนอร์หนึ่งได้อย่างไร ") ตัวอย่างเช่นคุณต้องการรักษาค่าดั้งเดิมไว้หรือไม่คุณต้องการลดการจัดสรรให้น้อยที่สุดเป็นต้นสำหรับกรณีที่ง่ายที่สุดคำตอบที่ดีที่สุดในความคิดของฉันคือใช้ คอนสตรัคคอนเทนเนอร์ที่ใช้เวลาสอง iterators (ภาชนะส่วนใหญ่สามารถใช้นั้น). NewContaner new_container(old_other_container.begin(), old_other_container.end()).
alfC

1
ใช่ std :: set ไม่ใช่ SequentialContainer และก็ไม่เป็นไร :-) existing_list = std::list(c.begin(), c.end(), existing_list.get_allocator()) ดีมากฉันคิดว่านั่นคือคำตอบของฉัน ไชโย!
Don Hatch
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.