การส่งคืนโดยการอ้างอิง rvalue มีประสิทธิภาพมากขึ้นหรือไม่?


คำตอบ:


246
Beta_ab&&
Beta::toAB() const {
    return move(Beta_ab(1, 1));
}

สิ่งนี้ส่งคืนการอ้างอิงแบบห้อยเช่นเดียวกับกรณีอ้างอิง lvalue หลังจากฟังก์ชั่นกลับมาวัตถุชั่วคราวจะถูกทำลาย คุณควรส่งคืนBeta_abตามมูลค่าดังต่อไปนี้

Beta_ab
Beta::toAB() const {
    return Beta_ab(1, 1);
}

ตอนนี้มันถูกย้ายBeta_abวัตถุชั่วคราวไปยังค่าส่งคืนของฟังก์ชัน หากคอมไพเลอร์สามารถทำได้คอมไพเลอร์จะหลีกเลี่ยงการย้ายทั้งหมดโดยใช้ RVO (การเพิ่มประสิทธิภาพค่าส่งคืน) ตอนนี้คุณสามารถทำสิ่งต่อไปนี้ได้

Beta_ab ab = others.toAB();

และจะย้ายโครงสร้างชั่วคราวเข้าไปabหรือทำ RVO เพื่อละเว้นการย้ายหรือคัดลอกทั้งหมด ฉันแนะนำให้คุณอ่านBoostCon09 Rvalue References 101ซึ่งอธิบายถึงเรื่องนี้และ (N) RVO โต้ตอบกับสิ่งนี้อย่างไร


กรณีของคุณในการส่งคืนข้อมูลอ้างอิง rvalue เป็นความคิดที่ดีในโอกาสอื่น ๆ ลองนึกภาพคุณมีgetAB()ฟังก์ชันที่คุณมักเรียกใช้ชั่วคราว ไม่เหมาะสมที่จะทำให้มันส่งคืนการอ้างอิง const lvalue สำหรับ rvalue temporaries คุณสามารถใช้งานได้เช่นนี้

struct Beta {
  Beta_ab ab;
  Beta_ab const& getAB() const& { return ab; }
  Beta_ab && getAB() && { return move(ab); }
};

โปรดทราบว่าmoveในกรณีนี้ไม่ใช่ทางเลือกเนื่องจากabไม่ใช่ค่าอัตโนมัติแบบโลคัลหรือค่าชั่วคราว ตอนนี้ref-qualifier &&บอกว่าฟังก์ชันที่สองถูกเรียกใช้ใน rvalue temporaries ทำการย้ายต่อไปนี้แทนการคัดลอก

Beta_ab ab = Beta().getAB();

51
ฉันมักจะสันนิษฐานว่าปัญหาการอ้างอิงห้อยจะหายไปโดยอัตโนมัติเมื่อประเภทการส่งคืนเป็นการอ้างอิง r-value ดีใจที่ได้สิ่งนั้นออกมาก่อนที่จะกัดฉัน กองแมลงยอดเยี่ยมดูด
deft_code

31
:) จริงๆแล้วการอ้างอิง rvalue เป็น "การอ้างอิงเพียงอย่างเดียว" เช่นการอ้างอิง lvalue คือ พวกเขาไม่ได้คัดลอกหรือจัดเก็บสิ่งใด ๆ
Johannes Schaub - litb

9
const & qualifier ในสมาชิกทำหน้าที่อะไรได้มากกว่า const ธรรมดา
galinette

4
+ 1-ed แต่ลิงก์เสีย: BoostCon09 Rvalue อ้างอิง 101
Siu Ching Pong -Asuka Kenji-

3
@galinette เหล่านี้มีโทษ-บ่น
Malcolm

2

มันสามารถมีประสิทธิภาพมากขึ้นตัวอย่างเช่นในบริบทที่แตกต่างบิต:

template <typename T>
T&& min_(T&& a, T &&b) {
    return std::move(a < b? a: b);
}

int main() {
   const std::string s = min_(std::string("A"), std::string("B"));
   fprintf(stderr, "min: %s\n", s.c_str());
   return 0;
}

ในฐานะที่เป็นข้อสังเกตที่น่าสนใจในเครื่องของฉันclang++ -O3สร้าง 54 คำแนะนำสำหรับการโค้ดข้างต้นเมื่อเทียบกับ 62 std::minคำแนะนำสำหรับการปกติ แต่ด้วยการ-O0สร้าง 518 คำแนะนำสำหรับการโค้ดข้างต้นเมื่อเทียบกับ 481 std::minปกติ


ฉันสับสนกับคำตอบของคุณ เคยลองรุ่นที่คล้ายกัน (อาจ) แต่ล้มเหลว: ideone.com/4GyUbZคุณช่วยอธิบายได้ไหมว่าทำไม?
Deqing

คุณใช้การอ้างอิงบนวัตถุชั่วคราวในfor(:)และรวมเข้ากับวัตถุที่ไม่ได้ปันส่วน แก้ไข: ideone.com/tQVOal
wonder.mice

3
คำตอบนี้ไม่ผิดจริงหรือ? สำหรับพารามิเตอร์ template T, T && ไม่ใช่การอ้างอิง r-value แต่เป็นการอ้างอิงสากลและในกรณีนั้นเราควรเรียก std :: forward <T> เสมอไม่ใช่ std :: move! ไม่ต้องพูดถึงคำตอบนี้ขัดแย้งโดยตรงกับคำตอบที่ได้รับการโหวตสูงสุดด้านบน
xdavidliu

@xdavidliu คำตอบนี้เป็นตัวอย่างที่กำหนดไว้ว่าการคืนค่าโดย rvalue จะมีประสิทธิภาพมากกว่าได้อย่างไร std::move()ใช้เป็นเพียงการแสดงที่ชัดเจนเพื่อแสดงให้เห็นประเด็นที่ชัดเจนยิ่งขึ้น ไม่ใช่รหัสที่คุณจะคัดลอกวางลงในโครงการของคุณ ไม่ขัดแย้งกับคำตอบที่ได้รับการโหวตสูงสุดเนื่องจากมีวัตถุชั่วคราวถูกสร้างขึ้นภายในฟังก์ชัน วัตถุที่ส่งคืนในที่นี้เป็นหนึ่งในอาร์กิวเมนต์ (วัตถุชั่วคราวจะถูกทำลายเป็นขั้นตอนสุดท้ายในการประเมินนิพจน์เต็มที่ (ศัพท์) ประกอบด้วยจุดที่สร้างขึ้น)
wonder.mice

2
@ wonder.mice แล้วโปรดแทนที่ T ด้วย std :: string; ที่นี่ไม่จำเป็นต้องใช้เทมเพลตเลยและการใช้ T && เป็นข้อมูลอ้างอิง r-value เป็นเพียงรูปแบบที่น่ากลัวซึ่งทำให้ผู้คนไม่จำเป็นต้องใช้เทมเพลตและค่า r โดยไม่จำเป็น
xdavidliu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.