ประสิทธิภาพของ C ++ 11 push_back () พร้อม std :: move เทียบกับ emplace_back () สำหรับอ็อบเจ็กต์ที่สร้างไว้แล้ว


88

emplace_back()โดยทั่วไปแล้วใน C ++ 11 เป็นที่ต้องการ (ในแง่ของประสิทธิภาพ) push_back()เนื่องจากอนุญาตให้มีการก่อสร้างในสถานที่แต่ก็ยังคงเป็นเช่นนี้เมื่อใช้push_back(std::move())กับวัตถุที่สร้างไว้แล้วหรือไม่

ตัวอย่างเช่นemplace_back()ยังคงเป็นที่ต้องการในกรณีต่อไปนี้?

นอกจากนี้มีประโยชน์ในตัวอย่างข้างต้นหรือไม่ที่จะทำ:

หรือการย้ายที่นี่ซ้ำซ้อนทั้งหมดหรือไม่มีผล?


myvector.emplace_back(mystring);คัดลอกและไม่ย้าย อีกสองท่าและควรจะเทียบเท่า
TC

ดูคำถามและผลลัพธ์นี้ด้วย: Requested Survey for VC ++ เกี่ยวกับ insert and emplace
ildjarn

คำตอบ:


117

มาดูกันว่าการโทรต่างๆที่คุณให้ไว้ทำอะไรได้บ้าง:

  1. emplace_back(mystring): นี่เป็นการสร้างองค์ประกอบใหม่แบบแทนที่ด้วยอาร์กิวเมนต์ที่คุณระบุ เนื่องจากคุณให้ค่า lvalue การก่อสร้างในสถานที่ในความเป็นจริงนั้นเป็นการสร้างสำเนากล่าวคือเหมือนกับการเรียกpush_back(mystring)

  2. push_back(std::move(mystring)): สิ่งนี้เรียกว่าการย้ายการแทรกซึ่งในกรณีนี้std::stringคือการย้ายในสถานที่

  3. emplace_back(std::move(mystring)): นี่เป็นการสร้างแบบแทนที่ด้วยข้อโต้แย้งที่คุณระบุอีกครั้ง เนื่องจากอาร์กิวเมนต์นั้นเป็นค่า r จึงเรียกตัวสร้างการย้ายstd::stringกล่าวคือเป็นการสร้างการย้ายในสถานที่เหมือนใน 2

กล่าวอีกนัยหนึ่งถ้าเรียกด้วยอาร์กิวเมนต์ประเภท T ไม่ว่าจะเป็น rvalue หรือ lvalue emplace_backและpush_backเทียบเท่า

อย่างไรก็ตามสำหรับข้อโต้แย้งอื่น ๆ ให้emplace_backชนะการแข่งขันเช่นด้วยchar const*a vector<string>:

  1. emplace_back("foo")เรียกร้องstd::string(char const*)ให้มีการก่อสร้างในสถานที่

  2. push_back("foo")ก่อนอื่นต้องเรียกร้องstd::string(char const*)ให้มีการแปลงโดยนัยที่จำเป็นเพื่อให้ตรงกับลายเซ็นของฟังก์ชันจากนั้นจึงทำการย้ายแทรกเช่นกรณีที่ 2 ดังนั้นจึงเทียบเท่ากับpush_back(string("foo"))


1
โดยทั่วไปแล้ว Move constructor จะมีประสิทธิภาพมากกว่าตัวสร้างสำเนาดังนั้นการใช้ rvalue (กรณีที่ 2, 3) จึงมีประสิทธิภาพมากกว่าการใช้ lvalue (กรณีที่ 1) แม้จะมีความหมายเหมือนกัน
Ryan Li

แล้วสถานการณ์เช่นนี้ล่ะ? โมฆะ foo (สตริง && s) {vector.emplace (s); // 1 vector.emplace (std :: move (s)); // 2}
VALOD9

@VALOD เหล่านี้คือหมายเลข 1 และ 3 ของรายการของฉันอีกครั้ง sอาจจะกำหนดเป็นอ้างอิง rvalue ที่ผูกเท่านั้นที่จะ rvalues แต่ภายในของfoo, sเป็น lvalue
Arne Mertz

1

emplace_back รับรายการอ้างอิง rvalue และพยายามสร้างองค์ประกอบคอนเทนเนอร์โดยตรง คุณสามารถเรียกใช้ emplace_back ได้ทุกประเภทที่ตัวสร้างองค์ประกอบคอนเทนเนอร์รองรับ เมื่อเรียกใช้ emplace_back สำหรับพารามิเตอร์ที่ไม่ใช่การอ้างอิง rvalue มันจะ 'ถอยกลับ' ไปที่การอ้างอิงตามปกติและอย่างน้อยตัวสร้างการคัดลอกจะไม่ถูกเรียกเมื่อพารามิเตอร์และองค์ประกอบคอนเทนเนอร์เป็นประเภทเดียวกัน ในกรณีของคุณ "myvector.emplace_back (mystring)" ควรทำสำเนาสตริงเนื่องจากคอมไพเลอร์ไม่สามารถทราบว่าพารามิเตอร์ myvector สามารถเคลื่อนย้ายได้ ดังนั้นใส่ std :: move สิ่งที่ให้ประโยชน์ที่คุณต้องการ push_back ควรใช้งานได้เช่นเดียวกับ emplace_back สำหรับองค์ประกอบที่สร้างไว้แล้ว


2
นั่นเป็น ... วิธีที่น่าสนใจในการอธิบายการส่งต่อ / การอ้างอิงสากล
TC
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.