TL; DR: การอ้างอิงผ่าน const ยังคงเป็นความคิดที่ดีใน C ++ ทุกสิ่งที่ถูกพิจารณา ไม่ใช่การเพิ่มประสิทธิภาพก่อนวัยอันควร
TL; DR2: ภาษิตส่วนใหญ่ไม่สมเหตุสมผลจนกว่าจะเป็นเช่นนั้น
จุดมุ่งหมาย
คำตอบนี้จะพยายามขยายรายการที่เชื่อมโยงในแนวทางหลักของC ++ (กล่าวถึงครั้งแรกในความคิดเห็นของ amon) เล็กน้อย
คำตอบนี้ไม่ได้พยายามที่จะแก้ไขปัญหาของวิธีการคิดและนำไปใช้อย่างถูกต้อง adages ต่าง ๆ ที่แพร่กระจายอย่างกว้างขวางในวงการโปรแกรมเมอร์โดยเฉพาะอย่างยิ่งปัญหาของการกระทบยอดระหว่างข้อสรุปหรือหลักฐานที่ขัดแย้งกัน
การบังคับใช้
คำตอบนี้ใช้กับการเรียกใช้ฟังก์ชัน (ขอบเขตซ้อนกันที่ไม่สามารถถอดออกได้ในเธรดเดียวกัน) เท่านั้น
(หมายเหตุด้านข้าง)เมื่อสิ่งที่ผ่านได้สามารถหลบหนีขอบเขต (เช่นมีอายุการใช้งานที่อาจเกินขอบเขตด้านนอก) มันเป็นสิ่งสำคัญมากที่จะตอบสนองความต้องการของโปรแกรมประยุกต์สำหรับการจัดการอายุการใช้งานวัตถุก่อนสิ่งอื่นใด โดยปกติต้องใช้การอ้างอิงที่มีความสามารถในการจัดการอายุการใช้งานเช่นตัวชี้สมาร์ท ทางเลือกอื่นอาจกำลังใช้ผู้จัดการ โปรดทราบว่าแลมบ์ดาเป็นขอบเขตที่ถอดออกได้ แลมบ์ดาจับการทำงานเหมือนมีขอบเขตวัตถุ ดังนั้นระวังด้วยแลมบ์ดาจับ ระวังด้วยว่าตัวแลมบ์ดานั้นถูกส่งผ่านอย่างไร - โดยการคัดลอกหรืออ้างอิง
เมื่อใดที่จะผ่านค่า
สำหรับค่าที่เป็นแบบสเกลาร์ (แบบดั้งเดิมมาตรฐานที่พอดีกับการลงทะเบียนเครื่องและมีค่าความหมาย) ซึ่งไม่จำเป็นต้องใช้การสื่อสารโดยความไม่แน่นอน (การอ้างอิงแบบแบ่งใช้) ให้ส่งผ่านค่า
สำหรับสถานการณ์ที่ผู้ใช้ต้องการการโคลนวัตถุหรือการรวมให้ส่งค่าตามค่าซึ่งสำเนาของผู้ปฏิบัติงานนั้นจะตอบสนองความต้องการของวัตถุที่ถูกโคลน
เมื่อใดที่จะผ่านการอ้างอิง ฯลฯ
สำหรับสถานการณ์อื่น ๆ ผ่านตัวชี้การอ้างอิงตัวชี้สมาร์ทจับ (ดู: สำนวนจับร่างกาย) ฯลฯ เมื่อใดก็ตามที่มีการปฏิบัติตามคำแนะนำนี้ให้ใช้หลักการของความถูกต้อง const ตามปกติ
สิ่งต่าง ๆ (รวมถึงวัตถุอาร์เรย์โครงสร้างข้อมูล) ที่มีขนาดใหญ่พอในหน่วยความจำควรได้รับการออกแบบมาเพื่ออำนวยความสะดวกในการอ้างอิงแบบอ้างอิงต่อกันเพื่อเหตุผลด้านประสิทธิภาพ คำแนะนำนี้จะนำไปใช้อย่างแน่นอนเมื่อมีหลายร้อยไบต์หรือมากกว่า คำแนะนำนี้เป็นเส้นเขตแดนเมื่อเป็นสิบไบต์
กระบวนทัศน์ที่ผิดปกติ
มีกระบวนทัศน์การเขียนโปรแกรมวัตถุประสงค์พิเศษซึ่งคัดลอกหนักโดยเจตนา ตัวอย่างเช่นการประมวลผลสตริงการทำให้เป็นอนุกรมการสื่อสารเครือข่ายการแยกการห่อของบุคคลที่สามไลบรารีการสื่อสารระหว่างกระบวนการหน่วยความจำที่ใช้ร่วมกัน ฯลฯ ในพื้นที่แอปพลิเคชันเหล่านี้หรือกระบวนทัศน์การเขียนโปรแกรมข้อมูลจะถูกคัดลอกจาก อาร์เรย์ไบต์
ข้อกำหนดทางภาษามีผลต่อคำตอบนี้อย่างไรก่อนพิจารณาการปรับให้เหมาะสม
Sub-TL; DR การขยายการอ้างอิงไม่ควรเรียกใช้โค้ด ผ่าน const-reference เป็นไปตามเกณฑ์นี้ อย่างไรก็ตามภาษาอื่น ๆ ทั้งหมดสนองเกณฑ์นี้ได้อย่างง่ายดาย
(โปรแกรมเมอร์มือใหม่ C ++ ขอแนะนำให้ข้ามส่วนนี้ไปโดยสิ้นเชิง)
(จุดเริ่มต้นของส่วนนี้ได้รับแรงบันดาลใจส่วนหนึ่งจากคำตอบของ gnasher729 อย่างไรก็ตามถึงข้อสรุปที่แตกต่างกัน)
C ++ อนุญาตให้ผู้สร้างสำเนาที่ผู้ใช้กำหนดและผู้ดำเนินการที่ได้รับมอบหมาย
(นี่คือ (เคย)) เป็นตัวเลือกที่กล้าหาญที่น่าประทับใจและน่าเสียดายมันแตกต่างจากบรรทัดฐานที่ยอมรับได้ในปัจจุบันในการออกแบบภาษา)
แม้ว่า c ++ Programmer ไม่ได้กำหนดหนึ่ง c ++ memcpy
คอมไพเลอร์จะต้องสร้างวิธีการดังกล่าวตามหลักการใช้ภาษาและจากนั้นตรวจสอบว่าความต้องการรหัสเพิ่มเติมที่จะดำเนินการอื่นที่ไม่ใช่ ตัวอย่างเช่นclass
/ struct
ที่มีstd::vector
สมาชิกจะต้องมีตัวคัดลอกคอนสตรัคเตอร์และผู้ประกอบการที่ได้รับมอบหมายที่ไม่สำคัญ
ในภาษาอื่น ๆ ตัวสร้างสำเนาและการโคลนวัตถุจะหมดกำลังใจ (ยกเว้นกรณีที่จำเป็นอย่างยิ่งและ / หรือมีความหมายต่อความหมายของแอปพลิเคชัน) เนื่องจากวัตถุมีความหมายอ้างอิงโดยการออกแบบภาษา ภาษาเหล่านี้มักจะมีกลไกการรวบรวมขยะที่ขึ้นอยู่กับความสามารถในการเข้าถึงแทนที่จะเป็นเจ้าของขอบเขตหรือการนับการอ้างอิง
เมื่อมีการส่งการอ้างอิงหรือตัวชี้ (รวมถึงการอ้างอิง const) ใน C ++ (หรือ C) โปรแกรมเมอร์จะมั่นใจได้ว่าจะไม่มีการเรียกใช้โค้ดพิเศษ (ฟังก์ชั่นที่ผู้ใช้กำหนดหรือสร้างโดยคอมไพเลอร์) ยกเว้นการเผยแพร่ค่าที่อยู่ (การอ้างอิงหรือตัวชี้) นี่คือความชัดเจนของพฤติกรรมที่โปรแกรมเมอร์ C ++ รู้สึกสบายใจ
อย่างไรก็ตามฉากหลังคือภาษา C ++ นั้นมีความซับซ้อนเกินความจำเป็นโดยที่ความชัดเจนของพฤติกรรมนี้เป็นเหมือนโอเอซิส
ในการเพิ่มคำอวยพร (หรือดูถูก) เพิ่มเติม C ++ จะแนะนำการอ้างอิงสากล (r-values) เพื่ออำนวยความสะดวกผู้ใช้งานตัวดำเนินการย้ายที่ผู้ใช้กำหนดเอง (ตัวสร้างการเคลื่อนไหว สิ่งนี้มีประโยชน์ต่อกรณีการใช้งานที่มีความเกี่ยวข้องสูง (การเคลื่อนย้าย (การย้าย) ของวัตถุจากอินสแตนซ์หนึ่งไปอีกอันหนึ่งโดยการลดความจำเป็นในการคัดลอกและการโคลนนิ่งลึก อย่างไรก็ตามในภาษาอื่น ๆ มันเป็นเรื่องไร้เหตุผลที่จะพูดถึงการเคลื่อนย้ายวัตถุดังกล่าว
(ส่วนนอกหัวข้อ) ส่วนที่อุทิศให้กับบทความ "Want Speed? Pass by Value!" เขียนในปี 2009
บทความนั้นเขียนขึ้นในปี 2009 และอธิบายถึงเหตุผลในการออกแบบสำหรับ r-value ใน C ++ บทความนั้นนำเสนอการโต้แย้งที่ถูกต้องถึงข้อสรุปของฉันในส่วนก่อนหน้า อย่างไรก็ตามตัวอย่างรหัสของบทความและการเรียกร้องประสิทธิภาพได้รับการข้องแวะมานานแล้ว
Sub-TL; DRการออกแบบของซีแมนทิกส์ r-value ใน C ++ อนุญาตให้มีความหมายที่น่าประหลาดใจด้านผู้ใช้บนSort
ฟังก์ชันตัวอย่างเช่น สง่างามนี้เป็นไปไม่ได้ที่จะจำลอง (เลียนแบบ) ในภาษาอื่น
ฟังก์ชั่นการจัดเรียงจะถูกนำไปใช้กับโครงสร้างข้อมูลทั้งหมด ดังกล่าวข้างต้นมันจะช้าถ้ามีการทำสำเนาจำนวนมากเกี่ยวข้อง ในฐานะที่เป็นการเพิ่มประสิทธิภาพ (ที่มีความเกี่ยวข้องในทางปฏิบัติ) ฟังก์ชั่นการเรียงลำดับได้รับการออกแบบให้ทำลายในภาษาอื่น ๆ นอกเหนือจาก C ++ การทำลายหมายถึงโครงสร้างข้อมูลเป้าหมายได้รับการแก้ไขเพื่อให้บรรลุเป้าหมายการเรียงลำดับ
ใน C ++ ผู้ใช้สามารถเลือกที่จะเรียกหนึ่งในสองของการใช้งาน: หนึ่งที่ทำลายล้างที่มีประสิทธิภาพที่ดีขึ้นหรือคนปกติที่ไม่ได้ปรับเปลี่ยนการป้อนข้อมูล (เทมเพลตถูกตัดออกเนื่องจากความกะทัดรัด)
/*caller specifically passes in input argument destructively*/
std::vector<T> my_sort(std::vector<T>&& input)
{
std::vector<T> result(std::move(input)); /* destructive move */
std::sort(result.begin(), result.end()); /* in-place sorting */
return result; /* return-value optimization (RVO) */
}
/*caller specifically passes in read-only argument*/
std::vector<T> my_sort(const std::vector<T>& input)
{
/* reuse destructive implementation by letting it work on a clone. */
/* Several things involved; e.g. expiring temporaries as r-value */
/* return-value optimization, etc. */
return my_sort(std::vector<T>(input));
}
/*caller can select which to call, by selecting r-value*/
std::vector<T> v1 = {...};
std::vector<T> v2 = my_sort(v1); /*non-destructive*/
std::vector<T> v3 = my_sort(std::move(v1)); /*v1 is gutted*/
นอกเหนือจากการเรียงลำดับแล้วความสง่างามนี้ยังมีประโยชน์ในการใช้อัลกอริทึมการหาค่ามัธยฐานแบบทำลายล้างในอาเรย์
อย่างไรก็ตามโปรดทราบว่าภาษาส่วนใหญ่จะใช้วิธีแผนผังการค้นหาแบบทวิภาคที่สมดุลกับการเรียงลำดับแทนที่จะใช้อัลกอริทึมการเรียงลำดับแบบทำลายล้างเพื่ออาร์เรย์ ดังนั้นความเกี่ยวข้องในทางปฏิบัติของเทคนิคนี้จึงไม่สูงอย่างที่ควรจะเป็น
การปรับให้เหมาะสมของคอมไพเลอร์มีผลกับคำตอบนี้อย่างไร
เมื่ออินไลน์ (และการเพิ่มประสิทธิภาพทั้งโปรแกรม / การเพิ่มประสิทธิภาพลิงค์เวลา) ถูกนำไปใช้ในการเรียกใช้ฟังก์ชั่นหลายระดับคอมไพเลอร์สามารถมองเห็น (บางครั้งละเอียด) การไหลของข้อมูล เมื่อเกิดเหตุการณ์นี้คอมไพเลอร์สามารถใช้การปรับให้เหมาะสมจำนวนมากซึ่งบางอย่างสามารถกำจัดการสร้างวัตถุทั้งหมดในหน่วยความจำ โดยทั่วไปเมื่อสถานการณ์นี้ใช้มันไม่สำคัญว่าพารามิเตอร์จะถูกส่งผ่านโดยค่าหรืออ้างอิง const เพราะคอมไพเลอร์สามารถวิเคราะห์อย่างละเอียด
อย่างไรก็ตามหากฟังก์ชันระดับล่างเรียกสิ่งที่อยู่เหนือการวิเคราะห์ (เช่นบางสิ่งในห้องสมุดอื่นนอกการคอมไพล์หรือกราฟการโทรที่ซับซ้อนเกินไป) จากนั้นคอมไพเลอร์ต้องปรับการป้องกันให้เหมาะสม
วัตถุที่ใหญ่กว่าค่าการลงทะเบียนเครื่องอาจถูกคัดลอกโดยคำแนะนำในการโหลด / จัดเก็บหน่วยความจำอย่างชัดเจนหรือโดยการเรียกไปยังmemcpy
ฟังก์ชันที่น่าเชื่อถือ ในบางแพลตฟอร์มคอมไพเลอร์จะสร้างคำสั่ง SIMD เพื่อย้ายระหว่างตำแหน่งหน่วยความจำสองแห่งซึ่งแต่ละคำสั่งนั้นจะย้ายนับสิบไบต์ (16 หรือ 32)
การอภิปรายเกี่ยวกับปัญหาของการใช้คำฟุ่มเฟื่อยหรือความยุ่งเหยิงภาพ
โปรแกรมเมอร์ C ++ คุ้นเคยกับสิ่งนี้เช่นตราบใดที่โปรแกรมเมอร์ไม่ได้เกลียด C ++ ค่าใช้จ่ายในการเขียนหรืออ่านค่าอ้างอิง const ในซอร์สโค้ดนั้นไม่น่ากลัว
การวิเคราะห์ต้นทุน - ผลประโยชน์อาจทำได้หลายครั้งก่อนหน้านี้ ฉันไม่รู้ว่ามีวิทยาศาสตร์อะไรที่ควรอ้างถึง ฉันเดาว่าการวิเคราะห์ส่วนใหญ่จะไม่ใช่ทางวิทยาศาสตร์หรือไม่สามารถทำซ้ำได้
นี่คือสิ่งที่ฉันจินตนาการ (โดยไม่มีการพิสูจน์หรือการอ้างอิงที่น่าเชื่อถือ) ...
- ใช่มันมีผลต่อประสิทธิภาพของซอฟต์แวร์ที่เขียนด้วยภาษานี้
- หากคอมไพเลอร์สามารถเข้าใจวัตถุประสงค์ของรหัสมันอาจเป็นไปได้ที่จะฉลาดพอที่จะทำให้มันเป็นไปโดยอัตโนมัติ
- น่าเสียดายที่ในภาษาที่สนับสนุนความไม่แน่นอน (ตรงข้ามกับความบริสุทธิ์ในการใช้งาน) คอมไพเลอร์จะจัดประเภทสิ่งต่าง ๆ ส่วนใหญ่ว่าเป็นการกลายพันธุ์ดังนั้นการหักห้ามใจโดยอัตโนมัติของการปฏิเสธจะปฏิเสธสิ่งต่าง ๆ มากที่สุด
- ค่าใช้จ่ายทางจิตขึ้นอยู่กับคน ผู้ที่พบว่าสิ่งนี้เป็นค่าใช้จ่ายทางจิตสูงจะปฏิเสธ C ++ เป็นภาษาโปรแกรมที่ทำงานได้