ในฐานะที่ @ JDługoszชี้ให้เห็นในความคิดเห็นสมุนไพรให้คำแนะนำอื่น ๆ ในการพูดคุย (ดูภายหลัง) อีกดูจากที่นี่: https://youtu.be/xnqTKD8uD64?t=54m50s https://youtu.be/xnqTKD8uD64?t=54m50s
คำแนะนำของเขาจะลดลงเหลือเพียงการใช้พารามิเตอร์ค่าสำหรับฟังก์ชั่นf
ที่เรียกว่า sink อาร์กิวเมนต์โดยสมมติว่าคุณจะย้ายโครงสร้างจาก sink อาร์กิวเมนต์เหล่านี้
วิธีการทั่วไปนี้เพิ่มค่าใช้จ่ายของตัวสร้างการย้ายสำหรับทั้งอาร์กิวเมนต์ lvalue และ rvalue เท่านั้นเมื่อเทียบกับการใช้งานที่เหมาะสมที่สุดของf
อาร์กิวเมนต์ lvalue และ rvalue ตามลำดับ หากต้องการดูสาเหตุของกรณีนี้สมมติว่าf
ใช้พารามิเตอร์ value โดยที่T
บางส่วนจะคัดลอกและย้ายชนิดที่สร้างได้:
void f(T x) {
T y{std::move(x)};
}
โทรf
กับข้อโต้แย้ง lvalue จะส่งผลให้ตัวสร้างสำเนาถูกเรียกว่าการสร้างและย้ายคอนสตรัคถูกเรียกว่าการสร้างx
y
ในทางกลับกันการเรียกf
ด้วยอาร์กิวเมนต์ rvalue จะทำให้ตัวสร้างการย้ายถูกเรียกเพื่อสร้างx
และตัวสร้างการย้ายอื่นจะถูกเรียกให้สร้างy
และย้ายคอนสตรัคอีกครั้งเพื่อให้ได้รับการเรียกตัวไปสร้าง
โดยทั่วไปการใช้งานที่ดีที่สุดของf
อาร์กิวเมนต์ lvalue มีดังนี้:
void f(const T& x) {
T y{x};
}
y
ในกรณีนี้เพียงหนึ่งตัวสร้างสำเนาที่เรียกว่าการสร้าง การใช้งานที่เหมาะสมที่สุดf
สำหรับอาร์กิวเมนต์ rvalue คือโดยทั่วไปอีกครั้งดังนี้:
void f(T&& x) {
T y{std::move(x)};
}
ในกรณีนี้คอนสตรัคเตอร์ย้ายเดียวเท่านั้นที่ถูกเรียกให้สร้าง y
ในกรณีนี้เพียงหนึ่งคอนสตรัคย้ายที่เรียกว่าการสร้าง
ดังนั้นการประนีประนอมที่สมเหตุสมผลคือการใช้พารามิเตอร์ค่าและมีการเรียกตัวสร้างการย้ายพิเศษหนึ่งรายการสำหรับอาร์กิวเมนต์ lvalue หรือ rvalue ที่เกี่ยวข้องกับการใช้งานที่เหมาะสมซึ่งเป็นคำแนะนำที่ได้รับในการพูดคุยของ Herb
ในฐานะที่เป็น @ JDługoszชี้ให้เห็นในความคิดเห็นที่ผ่านค่าตามความเหมาะสมเท่านั้นสำหรับฟังก์ชั่นที่จะสร้างวัตถุบางอย่างจากการโต้แย้งอ่าง เมื่อคุณมีฟังก์ชั่นf
ที่คัดลอกอาร์กิวเมนต์ของมันวิธีการ pass-by-value จะมีค่าใช้จ่ายมากกว่าวิธีการอ้างอิง pass-by-const ทั่วไป วิธีการแบบ pass-by-value สำหรับฟังก์ชั่นf
ที่เก็บสำเนาของพารามิเตอร์จะมีรูปแบบ:
void f(T x) {
T y{...};
...
y = std::move(x);
}
ในกรณีนี้มีการสร้างสำเนาและการกำหนดย้ายสำหรับอาร์กิวเมนต์ lvalue และการสร้างการย้ายและการกำหนดย้ายสำหรับอาร์กิวเมนต์ rvalue กรณีที่ดีที่สุดสำหรับอาร์กิวเมนต์ lvalue คือ:
void f(const T& x) {
T y{...};
...
y = x;
}
สิ่งนี้จะลดลงไปที่การมอบหมายเท่านั้นซึ่งอาจมีราคาถูกกว่าตัวสร้างการคัดลอกบวกการกำหนดย้ายที่จำเป็นสำหรับวิธีการส่งผ่านตามค่า สาเหตุของการทำเช่นนี้คือการมอบหมายอาจนำมาใช้หน่วยความจำที่มีอยู่ในการจัดสรรy
และดังนั้นจึงป้องกัน (de) การจัดสรรในขณะที่ตัวสร้างการคัดลอกมักจะจัดสรรหน่วยความจำ
สำหรับอาร์กิวเมนต์ rvalue การใช้งานที่ดีที่สุดสำหรับการf
เก็บสำเนามีรูปแบบ:
void f(T&& x) {
T y{...};
...
y = std::move(x);
}
ดังนั้นเฉพาะการย้ายที่ทำในกรณีนี้ การส่งผ่านค่า rvalue ไปยังเวอร์ชันf
ที่ใช้การอ้างอิงแบบ const จะคิดค่าใช้จ่ายเฉพาะการมอบหมายแทนการโอนย้าย ค่อนข้างจะพูดรุ่นของf
การอ้างอิง const ในกรณีนี้เนื่องจากการใช้งานทั่วไปจะดีกว่า
ดังนั้นโดยทั่วไปสำหรับการใช้งานที่เหมาะสมที่สุดคุณจะต้องโอเวอร์โหลดหรือทำการส่งต่อที่สมบูรณ์แบบดังที่แสดงในการพูดคุย ข้อเสียเปรียบคือการระเบิดแบบ combinatorial ในจำนวนเกินที่ต้องการขึ้นอยู่กับจำนวนของพารามิเตอร์สำหรับf
ในกรณีที่คุณเลือกที่จะโอเวอร์โหลดในหมวดค่าของการโต้แย้ง การส่งต่อที่สมบูรณ์แบบมีข้อเสียเปรียบที่f
กลายเป็นฟังก์ชั่นเทมเพลตซึ่งป้องกันไม่ให้กลายเป็นเสมือนจริงและส่งผลให้เกิดโค้ดที่ซับซ้อนมากขึ้นหากคุณต้องการทำให้ถูกต้อง 100% (ดูที่การพูดคุยเพื่อดูรายละเอียดเลือด)