ความหมายของการย้ายไม่จำเป็นต้องเป็นการปรับปรุงที่สำคัญเมื่อคุณคืนค่า - และเมื่อ / ถ้าคุณใช้shared_ptr
(หรือสิ่งที่คล้ายกัน) คุณอาจจะมองโลกในแง่ร้ายก่อนเวลาอันควร ในความเป็นจริงคอมไพเลอร์สมัยใหม่ที่มีเหตุผลเกือบทั้งหมดทำในสิ่งที่เรียกว่า Return Value Optimization (RVO) และ Named Return Value Optimization (NRVO) ซึ่งหมายความว่าเมื่อคุณส่งคืนค่าแทนที่จะคัดลอกค่าเลยพวกเขาเพียงแค่ผ่านตัวชี้ที่ซ่อนอยู่ / อ้างอิงถึงตำแหน่งที่ค่าจะได้รับมอบหมายหลังจากกลับมาและฟังก์ชั่นการใช้งานนั้นเพื่อสร้างมูลค่าที่มันจะจบลง มาตรฐาน C ++ มีข้อกำหนดพิเศษเพื่ออนุญาตสิ่งนี้ดังนั้น (ตัวอย่างเช่น) ตัวสร้างสำเนาของคุณมีผลข้างเคียงที่มองเห็นได้ไม่จำเป็นต้องใช้ตัวสร้างสำเนาเพื่อส่งคืนค่า ตัวอย่างเช่น:
#include <vector>
#include <numeric>
#include <iostream>
#include <stdlib.h>
#include <algorithm>
#include <iterator>
class X {
std::vector<int> a;
public:
X() {
std::generate_n(std::back_inserter(a), 32767, ::rand);
}
X(X const &x) {
a = x.a;
std::cout << "Copy ctor invoked\n";
}
int sum() { return std::accumulate(a.begin(), a.end(), 0); }
};
X func() {
return X();
}
int main() {
X x = func();
std::cout << "sum = " << x.sum();
return 0;
};
แนวคิดพื้นฐานที่นี่ค่อนข้างง่าย: สร้างคลาสที่มีเนื้อหาเพียงพอที่เราจะหลีกเลี่ยงการคัดลอกถ้าเป็นไปได้ ( std::vector
เราเติมด้วย int แบบสุ่ม 32767) เรามี ctor คัดลอกที่ชัดเจนที่จะแสดงให้เราเห็นเมื่อ / ถ้ามันถูกคัดลอก นอกจากนี้เรายังมีโค้ดอีกเล็กน้อยที่จะทำบางสิ่งด้วยค่าสุ่มในวัตถุดังนั้นเครื่องมือเพิ่มประสิทธิภาพจะไม่กำจัดทุกอย่างเกี่ยวกับคลาสเพียงเพราะมันไม่ทำอะไรเลย
จากนั้นเรามีรหัสบางอย่างเพื่อส่งคืนหนึ่งในวัตถุเหล่านี้จากฟังก์ชั่นและจากนั้นใช้การรวมเพื่อให้แน่ใจว่าวัตถุถูกสร้างขึ้นจริง ๆ ไม่เพียงละเว้นอย่างสมบูรณ์ เมื่อเราเรียกใช้อย่างน้อยที่สุดกับคอมไพเลอร์ตัวล่าสุด / สมัยใหม่เราพบว่าตัวสร้างการคัดลอกที่เราเขียนไม่เคยทำงานเลย - และใช่ฉันค่อนข้างแน่ใจว่าแม้การคัดลอกที่รวดเร็วด้วย a shared_ptr
ยังช้ากว่าการไม่คัดลอก เลย
การย้ายช่วยให้คุณสามารถทำสิ่งต่าง ๆ ที่คุณไม่สามารถทำได้โดยตรง พิจารณาส่วน "ผสาน" ของการจัดเรียงผสานภายนอก - คุณมี 8 ไฟล์ที่คุณจะรวมเข้าด้วยกัน เป็นการดีที่คุณต้องการที่จะนำทั้ง 8 ของไฟล์เหล่านั้นเป็นvector
- แต่เนื่องจากvector
( ณ C ++ 03) จะต้องสามารถที่จะคัดลอกองค์ประกอบและifstream
ไม่สามารถคัดลอกคุณติดอยู่กับบางส่วนunique_ptr
/ shared_ptr
, หรือบางอย่างในการสั่งซื้อเพื่อให้สามารถใส่พวกเขาในเวกเตอร์ โปรดทราบว่าแม้ว่าเราจะreserve
เว้นวรรค(เช่น) vector
ดังนั้นเราจึงมั่นใจได้ว่าifstream
จะไม่มีการคัดลอกจริงๆคอมไพเลอร์จะไม่ทราบดังนั้นรหัสจะไม่คอมไพล์แม้ว่าเราจะรู้ว่าตัวสร้างการคัดลอกจะไม่เป็นเช่นนั้น ใช้อยู่ดี
แม้ว่าจะยังไม่สามารถคัดลอกได้ แต่ใน C ++ 11 และifstream
สามารถย้ายได้ ในกรณีนี้วัตถุอาจจะไม่ถูกเคลื่อนย้าย แต่ความจริงที่ว่ามันอาจเป็นสิ่งที่จำเป็นหากทำให้คอมไพเลอร์มีความสุขดังนั้นเราจึงสามารถวางifstream
วัตถุของเราในvector
โดยตรงโดยไม่ต้องมีตัวชี้สมาร์ท
เวกเตอร์ที่ไม่ขยายเป็นตัวอย่างที่ดีงามในช่วงเวลานั้นความหมายย้ายจริงๆสามารถ / มี แต่ประโยชน์ ในกรณีนี้ RVO / NRVO จะไม่ช่วยเพราะเราไม่ได้จัดการกับค่าส่งคืนจากฟังก์ชั่น (หรือสิ่งที่คล้ายกันมาก) เรามีเวกเตอร์หนึ่งตัวที่ถือวัตถุบางอย่างและเราต้องการย้ายวัตถุเหล่านั้นไปยังหน่วยความจำอันใหม่ที่มีขนาดใหญ่ขึ้น
ใน C ++ 03 นั้นทำโดยการสร้างสำเนาของวัตถุในหน่วยความจำใหม่แล้วทำลายวัตถุเก่าในหน่วยความจำเก่า การทำสำเนาทั้งหมดเหล่านี้เพียงเพื่อทิ้งเอกสารเก่า ๆ นั้นค่อนข้างเสียเวลา ใน C ++ 11 คุณสามารถคาดหวังให้ย้ายได้ โดยทั่วไปแล้วสิ่งนี้จะช่วยให้เราทำสำเนาตื้น ๆ แทนสำเนาลึก (โดยทั่วไปช้ากว่ามาก) กล่าวอีกนัยหนึ่งด้วยสตริงหรือเวกเตอร์ (สำหรับตัวอย่างเพียงไม่กี่ตัวอย่าง) เราเพิ่งคัดลอกตัวชี้ไปยังวัตถุแทนที่จะทำสำเนาข้อมูลทั้งหมดที่ตัวชี้อ้างถึง
shared_ptr
เพียงเพื่อประโยชน์ในการคัดลอกอย่างรวดเร็ว) และถ้าความหมายการย้ายสามารถบรรลุเดียวกันโดยแทบไม่มีการเข้ารหัส, ความหมายและความสะอาด - การลงโทษ