การลอกเลียนแบบได้รับอนุญาตให้เกิดขึ้นได้ภายใต้หลายสถานการณ์ อย่างไรก็ตามแม้ว่าจะได้รับอนุญาต แต่โค้ดก็ยังต้องสามารถใช้งานได้ราวกับว่าไม่มีการลอกเลียนแบบ กล่าวคือต้องมีสำเนาที่สามารถเข้าถึงได้และ / หรือตัวสร้างการย้าย
รับประกันสำเนาสลัดนิยามใหม่จำนวนของ C ++ แนวคิดดังกล่าวว่าสถานการณ์บางอย่างที่ copies / ย้ายอาจจะ elided ไม่จริงกระตุ้นการคัดลอก / ย้ายที่ทั้งหมด คอมไพเลอร์ไม่ได้คัดลอก มาตรฐานกล่าวว่าไม่มีการคัดลอกแบบนี้เกิดขึ้น
พิจารณาฟังก์ชันนี้:
T Func() {return T();}
ภายใต้กฎการคัดลอกที่ไม่รับประกันการคัดลอกสิ่งนี้จะสร้างชั่วคราวจากนั้นย้ายจากชั่วคราวนั้นไปเป็นค่าส่งคืนของฟังก์ชัน การดำเนินการย้ายนั้นอาจถูกยกเลิก แต่T
ยังต้องมีตัวสร้างการย้ายที่สามารถเข้าถึงได้แม้ว่าจะไม่เคยใช้ก็ตาม
ในทำนองเดียวกัน:
T t = Func();
t
นี่คือการเริ่มต้นของการคัดลอก สิ่งนี้จะคัดลอกเริ่มต้นt
ด้วยค่าส่งคืนของFunc
. อย่างไรก็ตามT
ยังคงต้องมีตัวสร้างการย้ายแม้ว่าจะไม่ถูกเรียกก็ตาม
รับประกันการลอกเลียนแบบกำหนดความหมายของนิพจน์ prvalueใหม่ Pre-C ++ 17 prvalues เป็นวัตถุชั่วคราว ใน C ++ 17 นิพจน์ prvalue เป็นเพียงสิ่งที่สามารถทำให้เกิดเป็นชั่วคราวได้ แต่ยังไม่ใช่ชั่วคราว
หากคุณใช้ prvalue เพื่อเริ่มต้นอ็อบเจ็กต์ประเภท prvalue จะไม่มีการปรากฏชั่วคราว เมื่อคุณทำสิ่งreturn T();
นี้จะเริ่มต้นค่าส่งคืนของฟังก์ชันผ่าน prvalue เนื่องจากฟังก์ชันนั้นส่งคืนT
จึงไม่มีการสร้างชั่วคราว การเริ่มต้นของ prvalue เพียงแค่เริ่มต้นค่าส่งคืนโดยตรง
สิ่งที่ต้องเข้าใจก็คือว่าตั้งแต่ค่าตอบแทนเป็น prvalue มันเป็นไม่ได้วัตถุเลย เป็นเพียงตัวเริ่มต้นสำหรับวัตถุเช่นเดียวกับT()
คือ
เมื่อคุณทำT t = Func();
, prvalue ของค่าตอบแทนโดยตรงเริ่มต้นวัตถุt
; ไม่มีสเตจ "สร้างชั่วคราวและคัดลอก / ย้าย" ตั้งแต่Func()
ค่าตอบแทน 's เป็นเทียบเท่า prvalue ไปT()
, t
จะเริ่มต้นได้โดยตรงโดยตรงเช่นถ้าคุณเคยทำT()
T t = T()
หากใช้ prvalue ในลักษณะอื่น prvalue จะทำให้เป็นวัตถุชั่วคราวซึ่งจะถูกใช้ในนิพจน์นั้น (หรือทิ้งหากไม่มีนิพจน์) ดังนั้นหากคุณทำเช่นconst T &rt = Func();
นั้นค่า prvalue จะเป็นตัวกำหนดชั่วคราว (ใช้T()
เป็นตัวเริ่มต้น) ซึ่งข้อมูลอ้างอิงจะถูกเก็บไว้rt
พร้อมกับสิ่งที่ขยายอายุการใช้งานชั่วคราวตามปกติ
สิ่งหนึ่งที่รับประกันว่า elision อนุญาตให้คุณทำคือส่งคืนวัตถุที่ไม่สามารถเคลื่อนที่ได้ ตัวอย่างเช่นlock_guard
ไม่สามารถคัดลอกหรือย้ายได้ดังนั้นคุณจึงไม่มีฟังก์ชันที่ส่งคืนค่าตามค่า แต่ด้วยการรับประกันสำเนาคุณสามารถทำได้
รับประกัน elision ยังทำงานร่วมกับการเริ่มต้นโดยตรง:
new T(FactoryFunction());
หากFactoryFunction
ส่งคืนT
ตามค่านิพจน์นี้จะไม่คัดลอกค่าที่ส่งคืนไปยังหน่วยความจำที่จัดสรร มันจะจัดสรรหน่วยความจำแทนและใช้หน่วยความจำที่จัดสรรเป็นหน่วยความจำค่าส่งคืนสำหรับการเรียกใช้ฟังก์ชันโดยตรง
ดังนั้นฟังก์ชั่นโรงงานที่ส่งคืนตามค่าจึงสามารถเริ่มต้นหน่วยความจำที่จัดสรรฮีปได้โดยตรงโดยไม่รู้ตัว ตราบใดที่ฟังก์ชั่นเหล่านี้ภายในปฏิบัติตามกฎของการตัดออกสำเนารับประกันของหลักสูตร พวกเขาจะต้องกลับ prvalue T
ของประเภท
แน่นอนว่ามันก็ใช้ได้เช่นกัน:
new auto(FactoryFunction());
ในกรณีที่คุณไม่ชอบเขียนชื่อพิมพ์
สิ่งสำคัญคือต้องจำไว้ว่าการรับประกันข้างต้นใช้ได้ผลกับ prvalues เท่านั้น นั่นคือคุณไม่ได้รับการรับประกันเมื่อส่งคืนตัวแปรที่มีชื่อ :
T Func()
{
T t = ...;
...
return t;
}
ในกรณีนี้t
ยังต้องมีตัวสร้างการคัดลอก / ย้ายที่สามารถเข้าถึงได้ ใช่คอมไพลเลอร์สามารถเลือกที่จะเพิ่มประสิทธิภาพการคัดลอก / ย้าย แต่คอมไพเลอร์ยังคงต้องตรวจสอบการมีอยู่ของตัวสร้างสำเนา / ย้ายที่สามารถเข้าถึงได้
ดังนั้นจึงไม่มีอะไรเปลี่ยนแปลงสำหรับการเพิ่มประสิทธิภาพค่าตอบแทนที่ตั้งชื่อ (NRVO)