คำตอบง่ายๆคือคุณควรเขียนโค้ดสำหรับการอ้างอิงค่าเช่นเดียวกับการอ้างอิงรหัสปกติและคุณควรปฏิบัติต่อพวกเขาด้วยวิธีเดียวกัน 99% ของเวลา ซึ่งรวมถึงกฎเก่าทั้งหมดเกี่ยวกับการส่งคืนการอ้างอิง (เช่นจะไม่ส่งคืนการอ้างอิงไปยังตัวแปรโลคอล)
ยกเว้นว่าคุณกำลังเขียนคลาสคอนเทนเนอร์เทมเพลตที่จำเป็นต้องใช้ประโยชน์จาก std :: forward และสามารถเขียนฟังก์ชันทั่วไปที่รับการอ้างอิง lvalue หรือ rvalue ได้นี่เป็นความจริงที่มากหรือน้อย
ข้อดีอย่างหนึ่งของตัวสร้างการย้ายและการกำหนดย้ายคือถ้าคุณกำหนดคอมไพเลอร์สามารถใช้ในกรณีที่ RVO (การเพิ่มประสิทธิภาพค่าที่ส่งคืน) และ NRVO (การเพิ่มประสิทธิภาพค่าที่ส่งคืนชื่อ) ล้มเหลวในการเรียกใช้ นี่เป็นเรื่องที่ค่อนข้างใหญ่ในการส่งคืนวัตถุที่มีราคาแพงเช่นภาชนะและสายอักขระตามค่าอย่างมีประสิทธิภาพจากวิธีการ
ตอนนี้ที่สิ่งต่าง ๆ น่าสนใจด้วยการอ้างอิง rvalue ก็คือคุณสามารถใช้มันเป็นอาร์กิวเมนต์สำหรับฟังก์ชั่นปกติได้ สิ่งนี้ช่วยให้คุณสามารถเขียนคอนเทนเนอร์ที่มีโอเวอร์โหลดสำหรับการอ้างอิง const (const foo & อื่น ๆ ) และการอ้างอิง rvalue (foo && อื่น ๆ ) แม้ว่าการโต้แย้งนั้นเกินความสามารถเกินกว่าจะผ่านไปได้ด้วยการเรียก constructor เพียงอย่างเดียวก็ยังสามารถทำได้:
std::vector vec;
for(int x=0; x<10; ++x)
{
// automatically uses rvalue reference constructor if available
// because MyCheapType is an unamed temporary variable
vec.push_back(MyCheapType(0.f));
}
std::vector vec;
for(int x=0; x<10; ++x)
{
MyExpensiveType temp(1.0, 3.0);
temp.initSomeOtherFields(malloc(5000));
// old way, passed via const reference, expensive copy
vec.push_back(temp);
// new way, passed via rvalue reference, cheap move
// just don't use temp again, not difficult in a loop like this though . . .
vec.push_back(std::move(temp));
}
คอนเทนเนอร์ STL ได้รับการปรับปรุงให้มีการย้ายเกินพิกัดสำหรับเกือบทุกอย่าง (คีย์แฮชและค่าการแทรกเวกเตอร์ ฯลฯ ) และเป็นที่ที่คุณจะเห็นได้มากที่สุด
นอกจากนี้คุณยังสามารถใช้ฟังก์ชันเหล่านี้กับฟังก์ชั่นปกติและหากคุณมีเพียงอาร์กิวเมนต์การอ้างอิง rvalue คุณสามารถบังคับให้ผู้เรียกสร้างวัตถุและปล่อยให้ฟังก์ชันทำการย้าย นี่เป็นตัวอย่างมากกว่าการใช้งานที่ดีจริง ๆ แต่ในไลบรารีการแสดงผลของฉันฉันได้กำหนดสตริงให้กับทรัพยากรที่โหลดทั้งหมดเพื่อให้ง่ายต่อการดูว่าแต่ละวัตถุแสดงถึงการดีบักเกอร์ได้อย่างไร อินเทอร์เฟซเป็นดังนี้:
TextureHandle CreateTexture(int width, int height, ETextureFormat fmt, string&& friendlyName)
{
std::unique_ptr<TextureObject> tex = D3DCreateTexture(width, height, fmt);
tex->friendlyName = std::move(friendlyName);
return tex;
}
มันเป็นรูปแบบหนึ่งของ 'สิ่งที่เป็นนามธรรมที่น่าสนใจ' แต่ช่วยให้ฉันสามารถใช้ประโยชน์จากความจริงที่ว่าฉันต้องสร้างสตริงมาเกือบตลอดเวลาและหลีกเลี่ยงการทำสำเนาอีกครั้ง นี่ไม่ใช่รหัสประสิทธิภาพสูง แต่เป็นตัวอย่างที่ดีของความเป็นไปได้ที่ผู้คนจะได้รับประโยชน์จากคุณลักษณะนี้ รหัสนี้ต้องการให้ตัวแปรเป็นชั่วคราวในการโทรหรือ std :: move ถูกเรียกใช้:
// move from temporary
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, string("Checkerboard"));
หรือ
// explicit move (not going to use the variable 'str' after the create call)
string str("Checkerboard");
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, std::move(str));
หรือ
// explicitly make a copy and pass the temporary of the copy down
// since we need to use str again for some reason
string str("Checkerboard");
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, string(str));
แต่สิ่งนี้จะไม่รวบรวม!
string str("Checkerboard");
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, str);