มันเป็นการปฏิบัติที่ไม่ถูกต้องหรือไม่ในการเขียนโค้ดที่ต้องอาศัยการปรับให้เหมาะสมของคอมไพเลอร์?


99

ฉันได้เรียนรู้ C ++ บางอย่างและมักจะต้องส่งคืนวัตถุขนาดใหญ่จากฟังก์ชั่นที่สร้างขึ้นภายในฟังก์ชั่น ฉันรู้ว่ามีการส่งต่อโดยอ้างอิงส่งคืนตัวชี้และส่งคืนโซลูชันประเภทการอ้างอิง แต่ฉันยังอ่านว่าคอมไพเลอร์ C ++ (และมาตรฐาน C ++) อนุญาตให้ปรับค่าส่งคืนได้ซึ่งหลีกเลี่ยงการคัดลอกวัตถุขนาดใหญ่เหล่านี้ผ่านหน่วยความจำ ประหยัดเวลาและความทรงจำทั้งหมด

ตอนนี้ฉันรู้สึกว่าไวยากรณ์มีความชัดเจนมากขึ้นเมื่อวัตถุถูกส่งคืนโดยค่าอย่างชัดเจนและคอมไพเลอร์จะใช้ RVO และทำให้กระบวนการมีประสิทธิภาพมากขึ้น เป็นการปฏิบัติที่ไม่ถูกต้องหรือไม่ที่จะต้องพึ่งพาการเพิ่มประสิทธิภาพนี้ มันทำให้รหัสชัดเจนขึ้นและสามารถอ่านได้มากขึ้นสำหรับผู้ใช้ซึ่งมีความสำคัญอย่างยิ่ง แต่ฉันควรระวังว่าการคอมไพเลอร์จะจับโอกาส RVO หรือไม่

นี่เป็นการเพิ่มประสิทธิภาพขนาดเล็กหรือสิ่งที่ฉันควรทราบเมื่อออกแบบรหัสของฉัน


7
เพื่อตอบการแก้ไขของคุณมันเป็นการเพิ่มประสิทธิภาพแบบไมโครเพราะแม้ว่าคุณจะพยายามเปรียบเทียบสิ่งที่คุณได้รับในระดับนาโนวินาทีคุณก็แทบจะไม่เห็นมันเลย สำหรับส่วนที่เหลือฉันยังเน่าเสียใน C ++ เกินไปที่จะให้คำตอบที่เข้มงวดว่าทำไมมันถึงไม่ทำงาน หนึ่งในนั้นหากเป็นไปได้ว่ามีหลายกรณีเมื่อคุณต้องการการจัดสรรแบบไดนามิกดังนั้นจึงใช้ใหม่ / ตัวชี้ / การอ้างอิง
Walfrat

4
@ Walrat แม้ว่าวัตถุจะค่อนข้างใหญ่ตามคำสั่งของเมกะไบต์? อาร์เรย์ของฉันมีจำนวนมหาศาลเนื่องจากลักษณะของปัญหาที่ฉันแก้ไข
Matt

6
@ แมทฉันจะไม่ การอ้างอิง / พอยน์เตอร์มีอยู่จริงสำหรับสิ่งนี้ การเพิ่มประสิทธิภาพของคอมไพเลอร์ควรจะเกินกว่าสิ่งที่โปรแกรมเมอร์ควรพิจารณาเมื่อสร้างโปรแกรมแม้ว่าใช่บ่อยครั้งที่โลกทั้งสองทับซ้อนกัน
Neil

5
@Matt หากคุณไม่ได้ทำสิ่งที่เฉพาะเจาะจงอย่างยิ่งซึ่งคาดว่าจะต้องใช้นักพัฒนาที่มีประสบการณ์ 10 + ish ใน C / kerne การโต้ตอบของฮาร์ดแวร์ต่ำคุณไม่จำเป็น หากคุณคิดว่าคุณเป็นคนที่เฉพาะเจาะจงมาก ๆ ให้แก้ไขโพสต์ของคุณและเพิ่มคำอธิบายที่ถูกต้องเกี่ยวกับสิ่งที่แอปพลิเคชันของคุณควรทำ (เรียลไทม์? การคำนวณทางคณิตศาสตร์อย่างหนัก? ... )
Walfrat

37
ในกรณีเฉพาะของ C ++ 's (N) RVO ใช่การพึ่งพาการเพิ่มประสิทธิภาพนี้จะมีผลสมบูรณ์ นี่เป็นเพราะมาตรฐาน C ++ 17 สั่งว่ามันเกิดขึ้นโดยเฉพาะในสถานการณ์ที่คอมไพเลอร์สมัยใหม่ได้ทำไปแล้ว
Caleth

คำตอบ:


130

ใช้หลักการที่น่าประหลาดใจน้อยที่สุด

เป็นคุณและคนที่เคยใช้รหัสนี้และคุณแน่ใจหรือไม่ว่าคุณใน 3 ปีนั้นจะไม่ประหลาดใจกับสิ่งที่คุณทำ?

จากนั้นไปข้างหน้า

ในกรณีอื่น ๆ ให้ใช้วิธีมาตรฐาน ไม่เช่นนั้นคุณและเพื่อนร่วมงานของคุณจะพบเจอข้อบกพร่องอย่างหนัก

ตัวอย่างเช่นเพื่อนร่วมงานของฉันบ่นเกี่ยวกับรหัสของฉันทำให้เกิดข้อผิดพลาด แต่กลับกลายเป็นว่าเขาได้ปิดการประเมินผลบูลีนลัดวงจรในการตั้งค่าคอมไพเลอร์ของเขา ฉันเกือบตบเขา


88
@ ไม่มีนั่นคือจุดของฉันทุกคนอาศัยการประเมินการลัดวงจร และคุณไม่ควรต้องคิดให้รอบคอบสองครั้ง แต่ควรเปิดใช้งาน มันเป็นมาตรฐาน defacto ใช่คุณสามารถเปลี่ยนได้ แต่คุณไม่ควร
Pieter B

49
"ฉันเปลี่ยนวิธีการทำงานของภาษาและรหัสเน่าเสียของคุณก็พัง! Arghh!" ว้าว. การตบจะเหมาะสมส่งเพื่อนร่วมงานของคุณไปฝึกอบรม Zen ที่นั่นมีอยู่มากมาย

109
@PieterB ฉันค่อนข้างมั่นใจว่ารายละเอียดภาษาC และ C ++ รับประกันการประเมินผลการลัดวงจร ดังนั้นมันจึงไม่ได้เป็นเพียงมาตรฐาน de facto ก็มาตรฐาน หากไม่มีมันคุณจะไม่ได้ใช้ C / C ++ อีกต่อไป แต่มีบางอย่างที่น่าสงสัยเช่นนั้น: P
marcelm

47
สำหรับการอ้างอิงวิธีมาตรฐานที่นี่คือการส่งคืนตามค่า
DeadMG

28
@ dan04 ใช่มันอยู่ใน Delphi พวกอย่าไปตามตัวอย่างมันเกี่ยวกับจุดที่ฉันทำ อย่าทำสิ่งที่น่าแปลกใจที่ไม่มีใครทำ
Pieter B

81

สำหรับกรณีนี้โดยเฉพาะเพียงแค่คืนค่า

  • RVO และ NRVO เป็นที่รู้จักกันดีและมีการปรับแต่งที่เหมาะสมซึ่งควรทำโดยผู้รวบรวมที่เหมาะสมแม้ในโหมด C ++ 03

  • ซีแมนทิกส์ย้ายทำให้แน่ใจว่าวัตถุถูกย้ายออกจากฟังก์ชั่นหาก (N) RVO ไม่เกิดขึ้น ที่มีประโยชน์เฉพาะในกรณีที่วัตถุของคุณใช้ข้อมูลแบบไดนามิกภายใน (เช่นstd::vectorไม่) แต่ที่จริงควรจะเป็นกรณีที่ถ้ามันเป็นที่ใหญ่ - ล้นสแต็คคือความเสี่ยงกับวัตถุอัตโนมัติขนาดใหญ่

  • C ++ 17 บังคับใช้ RVO ดังนั้นไม่ต้องกังวลมันจะไม่หายไปกับคุณและจะเสร็จสิ้นการสร้างตัวเองอย่างสมบูรณ์เมื่อคอมไพเลอร์เป็นรุ่นล่าสุด

และในที่สุดบังคับให้มีการจัดสรรแบบไดนามิกเพิ่มเติมเพื่อส่งกลับตัวชี้หรือบังคับให้ประเภทผลลัพธ์ของคุณเป็นแบบเริ่มต้นที่สร้างขึ้นได้เพียงเพื่อให้คุณสามารถส่งผ่านได้เนื่องจากพารามิเตอร์เอาต์พุตมีทั้งโซลูชั่นที่น่าเกลียดและไม่ใช้สำนวนปัญหา มี.

เพียงแค่เขียนโค้ดที่สมเหตุสมผลและขอขอบคุณผู้เขียนคอมไพเลอร์สำหรับการปรับโค้ดให้เหมาะสมที่เหมาะสม


9
เพียงเพื่อความสนุกมาดูกันว่า Borland Turbo C ++ 3.0 จาก 1990-ish จัดการ RVOอย่างไร สปอยเลอร์: มันใช้งานได้ดี
nwp

9
กุญแจสำคัญในที่นี้คือมันไม่ใช่การเพิ่มประสิทธิภาพเฉพาะคอมไพเลอร์แบบสุ่มหรือ "คุณสมบัติที่ไม่มีเอกสาร" แต่เป็นสิ่งที่ในขณะที่ทางเลือกทางเทคนิคในหลาย ๆ เวอร์ชันของมาตรฐาน C ++ นั้นถูกผลักดันอย่างหนักจากอุตสาหกรรมและค่อนข้างมาก เป็นเวลานานมาก

7
การเพิ่มประสิทธิภาพนี้ไม่ค่อยแข็งแกร่งเท่าที่ควร ใช่มันค่อนข้างน่าเชื่อถือในกรณีที่เห็นได้ชัดที่สุด แต่หากมองหาตัวอย่างที่ Bugzilla ของ gcc มีหลายกรณีที่ไม่ชัดเจน
Marc Glisse

62

ตอนนี้ฉันรู้สึกว่าไวยากรณ์มีความชัดเจนมากขึ้นเมื่อวัตถุถูกส่งคืนโดยค่าอย่างชัดเจนและคอมไพเลอร์จะใช้ RVO และทำให้กระบวนการมีประสิทธิภาพมากขึ้น เป็นการปฏิบัติที่ไม่ถูกต้องหรือไม่ที่จะต้องพึ่งพาการเพิ่มประสิทธิภาพนี้ มันทำให้รหัสชัดเจนขึ้นและสามารถอ่านได้มากขึ้นสำหรับผู้ใช้ซึ่งมีความสำคัญอย่างยิ่ง แต่ฉันควรระวังว่าการคอมไพเลอร์จะจับโอกาส RVO หรือไม่

นี่ไม่ใช่การเพิ่มประสิทธิภาพขนาดเล็กที่รู้จักกันน่ารู้และเล็ก ๆ น้อย ๆ ที่คุณได้อ่านในบล็อกเล็ก ๆ

หลังจาก C ++ 11, RVO เป็นวิธีมาตรฐานในการเขียนรหัสของรหัสนี้ เป็นเรื่องปกติที่คาดว่าจะได้รับการสอนในการพูดถึงพูดถึงในบล็อกที่กล่าวถึงในมาตรฐานจะถูกรายงานว่าเป็นข้อผิดพลาดคอมไพเลอร์หากไม่ได้ใช้งาน ใน C ++ 17 ภาษาจะดำเนินต่อไปอีกขั้นหนึ่งแล้วสั่งการคัดลอกการคัดออกในบางสถานการณ์

คุณควรพึ่งพาการเพิ่มประสิทธิภาพนี้อย่างแน่นอน

ยิ่งไปกว่านั้น return-by-value จะนำไปสู่การอ่านและจัดการรหัสได้ง่ายกว่าโค้ดที่กลับมาโดยอ้างอิง คุณค่าความหมายเป็นสิ่งที่ทรงพลังซึ่งอาจนำไปสู่โอกาสในการเพิ่มประสิทธิภาพมากขึ้น


3
ขอขอบคุณสิ่งนี้มีเหตุผลและสอดคล้องกับ "หลักการความประหลาดใจน้อยที่สุด" ที่กล่าวถึงข้างต้น มันจะทำให้โค้ดมีความชัดเจนและเข้าใจได้และทำให้ยากที่จะทำเลอะกับ shenanigans ของตัวชี้
Matt

3
@ มัดส่วนหนึ่งของเหตุผลที่ฉัน upvoted คำตอบนี้ก็คือมันไม่ได้พูดถึง "คุณค่าความหมาย" เมื่อคุณได้รับประสบการณ์มากขึ้นใน C ++ (และการเขียนโปรแกรมโดยทั่วไป) คุณจะพบสถานการณ์เป็นครั้งคราวซึ่งความหมายของค่าไม่สามารถใช้สำหรับวัตถุบางอย่างได้เนื่องจากวัตถุเหล่านั้นไม่แน่นอนและต้องมีการเปลี่ยนแปลงในรหัสอื่นที่ใช้วัตถุเดียวกันนั้น ตัวอย่างของ "ความไม่แน่นอนที่ใช้ร่วมกัน") เมื่อสถานการณ์เหล่านี้เกิดขึ้นวัตถุที่ได้รับผลกระทบจะต้องใช้ร่วมกันผ่านตัวชี้ (สมาร์ท)
ร.

16

ความถูกต้องของรหัสที่คุณเขียนไม่ควรขึ้นอยู่กับการปรับให้เหมาะสม มันควรจะส่งออกผลลัพธ์ที่ถูกต้องเมื่อดำเนินการใน C ++ "เครื่องเสมือน" ที่พวกเขาใช้ในสเปค

อย่างไรก็ตามสิ่งที่คุณพูดถึงเป็นคำถามที่มีประสิทธิภาพมากกว่า รหัสของคุณจะทำงานได้ดีขึ้นหากปรับให้เหมาะสมด้วยคอมไพเลอร์ปรับแต่ง RVO ให้เหมาะสม ไม่เป็นไรเพราะเหตุผลทั้งหมดชี้ให้เห็นในคำตอบอื่น ๆ

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

ฉันคิดว่าตัวอย่างที่ดีที่สุดในเรื่องนี้ในทางปฏิบัติของฉันคือการปรับให้เหมาะสมหาง:

   int sillyAdd(int a, int b)
   {
      if (b == 0)
          return a;
      return sillyAdd(a + 1, b - 1);
   }

มันเป็นตัวอย่างที่งี่เง่า แต่มันแสดงให้เห็นถึงการเรียกแบบหางโดยที่ฟังก์ชั่นนั้นถูกเรียกซ้ำในตอนท้ายของฟังก์ชั่น เครื่องเสมือน C ++ จะแสดงให้เห็นว่ารหัสนี้ทำงานอย่างถูกต้องแม้ว่าฉันอาจทำให้เกิดความสับสนเล็กน้อยว่าทำไมฉันจึงต้องใส่ใจกับการเขียนรูทีนเพิ่มเติมดังกล่าวในตอนแรก อย่างไรก็ตามในการใช้งานจริงของ C ++ เรามีสแต็กและมีพื้นที่ จำกัด หากทำไปเรื่อย ๆ ฟังก์ชันนี้จะต้องผลักb + 1เฟรมสแต็กอย่างน้อยลงในสแต็กเช่นเดียวกับที่เพิ่มเข้าไป ถ้าฉันต้องการคำนวณsillyAdd(5, 7)นี่ไม่ใช่เรื่องใหญ่ ถ้าฉันต้องการคำนวณsillyAdd(0, 1000000000)ฉันอาจมีปัญหาในการก่อให้เกิด StackOverflow (และไม่ใช่ประเภทที่ดี )

อย่างไรก็ตามเราสามารถเห็นได้ว่าเมื่อเราไปถึงบรรทัดสุดท้ายที่ส่งคืนเราจะทำทุกอย่างในสแต็กเฟรมปัจจุบัน เราไม่จำเป็นต้องเก็บมันไว้รอบ ๆ การปรับการเรียกท้ายให้คุณ "นำ" เฟรมสแต็คที่มีอยู่กลับมาใช้ใหม่สำหรับฟังก์ชั่นถัดไป ด้วยวิธีนี้เราจะต้องกองกรอบ 1 b+1มากกว่า (เรายังต้องทำการบวกและลบออกทั้งหมดที่โง่ แต่พวกเขาไม่ได้ใช้พื้นที่มากขึ้น) ผลการเพิ่มประสิทธิภาพจะเปลี่ยนรหัสเป็น:

   int sillyAdd(int a, int b)
   {
      begin:
      if (b == 0)
          return a;
      // return sillyAdd(a + 1, b - 1);
      a = a + 1;
      b = b - 1;
      goto begin;  
   }

ในบางภาษาข้อกำหนดในการเพิ่มประสิทธิภาพการโทรหางจะต้องระบุไว้อย่างชัดเจน C ++ ไม่ใช่หนึ่งในนั้น ฉันไม่สามารถพึ่งพาคอมไพเลอร์ C ++ เพื่อรับรู้ถึงโอกาสในการเพิ่มประสิทธิภาพการโทรหางนี้ได้เว้นแต่ฉันจะไปเป็นกรณี ๆ ไป ด้วย Visual Studio รุ่นของฉันเวอร์ชันที่วางจำหน่ายจะทำการปรับแต่ง tail call ให้เหมาะสม แต่เวอร์ชันการดีบักไม่ได้ (โดยการออกแบบ)

เพราะฉะนั้นมันก็จะไม่ดีสำหรับผมที่จะขึ้นอยู่กับsillyAdd(0, 1000000000)ความสามารถในการคำนวณ


2
นี่เป็นมุมที่น่าสนใจ แต่ฉันไม่คิดว่าคุณจะสามารถพูดคุยกับกฎในย่อหน้าแรกของคุณได้ สมมติว่าฉันมีโปรแกรมสำหรับอุปกรณ์ขนาดเล็กที่จะโหลดถ้าหากฉันใช้การเพิ่มประสิทธิภาพการลดขนาดของคอมไพเลอร์ - มันผิดที่จะทำเช่นนั้น? ดูเหมือนว่าค่อนข้างจะพูดได้ว่าตัวเลือกที่ถูกต้องของฉันเท่านั้นคือการเขียนซ้ำในแอสเซมเบลอร์โดยเฉพาะอย่างยิ่งหากการเขียนนั้นทำในสิ่งเดียวกันกับที่เครื่องมือเพิ่มประสิทธิภาพทำการแก้ไขปัญหา
sdenham

5
@denham ฉันคิดว่ามีห้องเล็ก ๆ ในการโต้แย้ง หากคุณไม่ได้เขียนสำหรับ "C ++" อีกต่อไป แต่จะเขียนสำหรับ "WindRiver C ++ คอมไพเลอร์รุ่น 3.4.1" จากนั้นฉันจะเห็นตรรกะที่นั่น อย่างไรก็ตามตามกฎทั่วไปหากคุณกำลังเขียนสิ่งที่ไม่สามารถทำงานได้อย่างถูกต้องตามข้อกำหนดคุณอยู่ในสถานการณ์ที่แตกต่างกันมาก ฉันรู้ว่าห้องสมุด Boost มีรหัสเช่นนั้น แต่พวกเขามักวางไว้ใน#ifdefบล็อกและมีวิธีแก้ปัญหาที่ได้มาตรฐาน
Cort Ammon

4
นั่นคือการพิมพ์ผิดในบล็อกที่สองของรหัสที่มันบอกว่าb = b + 1?
stib

2
คุณอาจต้องการอธิบายความหมายของ "เครื่องเสมือน C ++" เนื่องจากไม่ใช่คำที่ใช้ในเอกสารมาตรฐานใด ๆ ฉันคิดว่าคุณกำลังพูดถึงรูปแบบการดำเนินการของ C ++ แต่ไม่แน่ใจอย่างสมบูรณ์และคำศัพท์ของคุณคล้ายกับ "bytecode virtual machine" ซึ่งเกี่ยวข้องกับบางสิ่งที่แตกต่างอย่างสิ้นเชิง
Toby Speight

1
@supercat Scala ยังมีไวยากรณ์การเรียกซ้ำแบบหางที่ชัดเจน C ++ เป็นสัตว์เดรัจฉานของตัวเอง แต่ฉันคิดว่า tail recursion เป็นภาษาที่ไม่ใช้งานได้และเป็นข้อบังคับสำหรับภาษาที่ใช้งานได้ทำให้เหลือชุดของภาษาเล็ก ๆ การแปลการวนซ้ำแบบหางเป็นลูปและการกลายพันธุ์อย่างชัดเจนนั้นเป็นตัวเลือกที่ดีกว่าสำหรับหลายภาษา
prosfilaes

8

ในทางปฏิบัติโปรแกรม C ++ คาดว่าจะมีการปรับแต่งคอมไพเลอร์ให้เหมาะสม

ดูสะดุดตาในส่วนหัวมาตรฐานของการใช้งานคอนเทนเนอร์มาตรฐานของคุณ ด้วยGCCคุณสามารถขอแบบฟอร์มที่ประมวลผลล่วงหน้า ( g++ -C -E) และการเป็นตัวแทนภายใน GIMPLE ( g++ -fdump-tree-gimpleหรือ Gimple SSA ด้วย-fdump-tree-ssa) ของไฟล์ต้นฉบับส่วนใหญ่ (หน่วยการแปลทางเทคนิค) โดยใช้คอนเทนเนอร์ คุณจะประหลาดใจกับจำนวนของการเพิ่มประสิทธิภาพที่ทำได้ (ด้วยg++ -O2) ดังนั้นผู้พัฒนาระบบของคอนเทนเนอร์จึงต้องพึ่งพาการปรับให้เหมาะสมที่สุด (และส่วนใหญ่แล้วผู้ดำเนินการของไลบรารีมาตรฐาน C ++ จะรู้ว่าการเพิ่มประสิทธิภาพจะเกิดอะไรขึ้นและเขียนการใช้งานคอนเทนเนอร์ด้วยผู้ที่อยู่ในใจ จัดการกับคุณสมบัติที่ต้องการโดยไลบรารีมาตรฐาน C ++ แล้ว)

ในทางปฏิบัติมันเป็นการเพิ่มประสิทธิภาพของคอมไพเลอร์ซึ่งทำให้ C ++ และคอนเทนเนอร์มาตรฐานมีประสิทธิภาพเพียงพอ ดังนั้นคุณสามารถพึ่งพาพวกเขา

และเช่นเดียวกันกับกรณี RVO ที่กล่าวถึงในคำถามของคุณ

มาตรฐาน C ++ ได้รับการออกแบบร่วมกัน (โดยเฉพาะอย่างยิ่งโดยการทดสอบการเพิ่มประสิทธิภาพที่ดีพอในขณะที่เสนอคุณสมบัติใหม่) เพื่อให้ทำงานได้ดีกับการปรับให้เหมาะสมที่สุด

ตัวอย่างเช่นพิจารณาโปรแกรมด้านล่าง:

#include <algorithm>
#include <vector>

extern "C" bool all_positive(const std::vector<int>& v) {
  return std::all_of(v.begin(), v.end(), [](int x){return x >0;});
}

g++ -O3 -fverbose-asm -Sรวบรวมไว้ด้วย คุณจะพบว่าฟังก์ชั่นที่สร้างขึ้นไม่ได้รันCALLคำสั่งเครื่องใด ๆ ดังนั้นขั้นตอน C ++ ส่วนใหญ่ (การสร้างการปิดแลมบ์ดา, การสมัครซ้ำ, การรับbeginและการendวนซ้ำ ฯลฯ ... ) ได้รับการปรับปรุง รหัสเครื่องมีการวนซ้ำ (ซึ่งไม่ปรากฏอย่างชัดเจนในซอร์สโค้ด) หากไม่มีการปรับให้เหมาะสม C ++ 11 จะไม่ประสบความสำเร็จ

ภาคผนวก

(เพิ่ม 31 ธันวาคมเซนต์ 2017)

ดูCppCon 2017: Matt Godbolt“ เมื่อไม่นานมานี้คอมไพเลอร์ของฉันทำอะไรให้ฉัน? Unbolting ฝาคอมไพเลอร์ของ”การพูดคุย


4

เมื่อใดก็ตามที่คุณใช้คอมไพเลอร์ความเข้าใจก็คือมันจะสร้างรหัสเครื่องหรือไบต์ให้คุณ มันไม่ได้รับประกันอะไรเกี่ยวกับสิ่งที่สร้างรหัสเป็นเช่นนั้นยกเว้นว่ามันจะใช้รหัสแหล่งที่มาตามข้อกำหนดของภาษา โปรดทราบว่าการรับประกันนี้เหมือนกันโดยไม่คำนึงถึงระดับของการปรับให้เหมาะสมที่ใช้ดังนั้นโดยทั่วไปไม่มีเหตุผลที่จะพิจารณาว่าเอาต์พุตหนึ่ง 'สิทธิ' มากกว่าอีกอันหนึ่ง

นอกจากนี้ในกรณีเหล่านั้นเช่น RVO ซึ่งมีการระบุไว้ในภาษาดูเหมือนว่าจะไม่มีประโยชน์ที่จะหลีกเลี่ยงการใช้งานโดยเฉพาะอย่างยิ่งถ้ามันทำให้ซอร์สโค้ดง่ายขึ้น

มีความพยายามอย่างมากในการทำให้คอมไพเลอร์สร้างผลผลิตที่มีประสิทธิภาพและชัดเจนว่าเจตนาคือความสามารถเหล่านั้นที่จะใช้

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


3

ฉันคิดว่าคนอื่นครอบคลุมมุมเฉพาะเกี่ยวกับ C ++ และ RVO ดี นี่คือคำตอบทั่วไปเพิ่มเติม:

เมื่อพูดถึงความถูกต้องคุณไม่ควรพึ่งพาการปรับให้เหมาะสมของคอมไพเลอร์หรือพฤติกรรมที่เฉพาะเจาะจงของคอมไพเลอร์โดยทั่วไป โชคดีที่คุณดูเหมือนจะไม่ทำสิ่งนี้

เมื่อพูดถึงประสิทธิภาพคุณต้องพึ่งพาพฤติกรรมเฉพาะของคอมไพเลอร์โดยทั่วไปและการปรับแต่งคอมไพเลอร์ให้ดีเป็นพิเศษ คอมไพเลอร์ที่สอดคล้องกับมาตรฐานมีอิสระในการคอมไพล์โค้ดของคุณในทุกทางที่มันต้องการตราบใดที่โค้ดที่คอมไพล์ทำงานตามข้อกำหนดภาษา และฉันไม่ทราบถึงข้อกำหนดใด ๆ สำหรับภาษากระแสหลักที่ระบุว่าการดำเนินการแต่ละอย่างรวดเร็วแค่ไหน


1

การปรับให้เหมาะสมของคอมไพเลอร์ควรมีผลกับประสิทธิภาพเท่านั้นไม่ใช่ผล การใช้การปรับให้เหมาะสมของคอมไพเลอร์เพื่อตอบสนองความต้องการที่ไม่สามารถใช้งานได้ไม่เพียง แต่สมเหตุสมผลเท่านั้น แต่เป็นเหตุผลที่ทำให้มีการเลือกคอมไพเลอร์ตัวหนึ่ง

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

หากการเพิ่มประสิทธิภาพคอมไพเลอร์ทำให้เกิดผลลัพธ์ที่แตกต่างนั่นคือข้อผิดพลาด - ข้อบกพร่องในคอมไพเลอร์ การใช้บั๊กในคอมไพเลอร์เป็นความผิดพลาดในระยะยาว - จะเกิดอะไรขึ้นเมื่อมันได้รับการแก้ไข

การใช้ธงคอมไพเลอร์ที่เปลี่ยนวิธีการคำนวณควรมีการบันทึกไว้เป็นอย่างดี แต่ใช้ตามความจำเป็น


แต่น่าเสียดายที่เอกสารรวบรวมข้อมูลจำนวนมากทำงานได้ไม่ดีในการระบุสิ่งที่เป็นหรือไม่รับประกันในโหมดต่างๆ นอกจากนี้ผู้เขียนคอมไพเลอร์ "ทันสมัย" ดูเหมือนจะไม่สนใจการรวมกันของการค้ำประกันที่โปรแกรมเมอร์ทำและไม่ต้องการ หากโปรแกรมทำงานได้ดีหากx*y>zผลตอบแทนเป็น 0 หรือ 1 ในกรณีที่มีการไหลล้นโดยพลการหากว่ามันไม่มีผลข้างเคียงอื่น ๆ ที่ต้องการให้โปรแกรมเมอร์ต้องป้องกันโอเวอร์โฟลว์ที่ค่าใช้จ่ายทั้งหมดหรือบังคับให้คอมไพเลอร์ประเมินค่านิพจน์ การเพิ่มประสิทธิภาพที่ไม่จำเป็นโดยไม่จำเป็นต้องเสียค่าเทียบกับการบอกว่า ...
supercat

... คอมไพเลอร์อาจที่พักผ่อนของมันทำตัวราวกับว่าx*yส่งเสริมการถูกดำเนินการในการบางประเภทโดยพลการอีกต่อไป (จึงทำให้รูปแบบของการยกและการลดความแรงที่จะเปลี่ยนพฤติกรรมของผู้ป่วยล้นบางส่วน) คอมไพเลอร์จำนวนมากต้องการให้โปรแกรมเมอร์ทำการป้องกันโอเวอร์โฟลว์ที่ค่าใช้จ่ายทั้งหมดหรือบังคับให้คอมไพเลอร์ตัดทอนค่ากลางทั้งหมดในกรณีที่โอเวอร์โฟลว์
supercat

1

เลขที่

นั่นคือสิ่งที่ฉันทำตลอดเวลา หากฉันต้องการเข้าถึงบล็อกหน่วยความจำ 16 บิตโดยพลการฉันจะทำสิ่งนี้

void *ptr = get_pointer();
uint16_t u16;
memcpy(&u16, ptr, sizeof(u16)); // ntohs omitted for simplicity

... และพึ่งพาคอมไพเลอร์ที่ทำทุกอย่างเท่าที่ทำได้เพื่อเพิ่มประสิทธิภาพโค้ด รหัสนี้ใช้งานได้กับ ARM, i386, AMD64 และในทุก ๆ สถาปัตยกรรม ในทางทฤษฎีแล้วคอมไพเลอร์ที่ไม่ได้รับการออปติไมซ์สามารถเรียกได้จริงmemcpyส่งผลให้เกิดประสิทธิภาพที่ไม่ดีโดยสิ้นเชิง แต่นั่นก็ไม่มีปัญหาสำหรับฉัน

พิจารณาทางเลือก:

void *ptr = get_pointer();
uint16_t *u16ptr = ptr;
uint16_t u16;
u16 = *u16ptr;  // ntohs omitted for simplicity

รหัสทางเลือกนี้ไม่สามารถทำงานกับเครื่องที่ต้องการการจัดตำแหน่งที่เหมาะสมหากget_pointer()ส่งคืนตัวชี้ที่ไม่จัดแนว นอกจากนี้อาจมีปัญหาเรื่องนามแฝงในทางเลือก

ความแตกต่างระหว่าง -O2 และ -O0 เมื่อใช้memcpyเคล็ดลับนั้นยอดเยี่ยม: ประสิทธิภาพการตรวจสอบ IP 3.2 Gbps เทียบกับประสิทธิภาพการตรวจสอบ IP 67 Gbps ด้วยลำดับความแตกต่างที่มีขนาด!

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

ข้อเสียเปรียบของการพึ่งพาการเพิ่มประสิทธิภาพคอมไพเลอร์คือถ้าคุณเรียกใช้ gdb เพื่อแก้ไขข้อบกพร่องรหัสของคุณคุณอาจพบว่ามีการปรับแต่งจำนวนมากออกไป ดังนั้นคุณอาจจำเป็นต้องคอมไพล์ใหม่ด้วย -O0 ซึ่งหมายความว่าประสิทธิภาพการทำงานจะแย่ลงเมื่อทำการดีบั๊ก ฉันคิดว่านี่เป็นข้อเสียเปรียบที่ควรพิจารณาโดยคำนึงถึงประโยชน์ของการเพิ่มประสิทธิภาพคอมไพเลอร์

ไม่ว่าคุณจะทำอะไรโปรดตรวจสอบให้แน่ใจว่าวิธีการของคุณไม่ใช่พฤติกรรมที่ไม่ได้กำหนด การเข้าถึงบล็อกหน่วยความจำแบบสุ่มแน่นอนว่าเป็นจำนวนเต็ม 16 บิตเป็นพฤติกรรมที่ไม่ได้กำหนดเนื่องจากปัญหาเรื่องนามแฝงและการจัดตำแหน่ง


0

ความพยายามทั้งหมดในการเขียนโค้ดที่มีประสิทธิภาพในทุกสิ่ง แต่ชุดประกอบต้องอาศัยการเพิ่มประสิทธิภาพคอมไพเลอร์เป็นอย่างมากโดยเริ่มจากการจัดสรรขั้นพื้นฐานอย่างมีประสิทธิภาพที่สุดเพื่อหลีกเลี่ยงการล้นสแต็คที่ฟุ่มเฟือยทั่วทั้งสถานที่ มิฉะนั้นเราจะกลับไปที่ยุค 80 ที่เราต้องใส่registerคำแนะนำทั่วสถานที่และใช้จำนวนตัวแปรขั้นต่ำในฟังก์ชั่นเพื่อช่วยคอมไพเลอร์ C แบบเก่าหรือแม้กระทั่งก่อนหน้านี้เมื่อgotoมีการเพิ่มประสิทธิภาพการแยกสาขาที่มีประโยชน์

หากเราไม่รู้สึกว่าเราสามารถพึ่งพาความสามารถของเครื่องมือเพิ่มประสิทธิภาพของเราในการเพิ่มประสิทธิภาพรหัสของเราเราทุกคนยังคงเข้ารหัสเส้นทางการปฏิบัติที่สำคัญต่อประสิทธิภาพในการประกอบ

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

RVO เป็นสิ่งที่มีมานานแล้วและอย่างน้อยก็ยกเว้นกรณีที่ซับซ้อนมาก ๆ เป็นสิ่งที่คอมไพเลอร์ได้รับการประยุกต์ใช้อย่างดีสำหรับทุกวัย แน่นอนว่ามันไม่คุ้มค่าที่จะแก้ไขปัญหาที่ไม่มีอยู่จริง

ข้อผิดพลาดที่ด้านข้างของการใช้เครื่องมือเพิ่มประสิทธิภาพไม่กลัวมัน

ในทางตรงกันข้ามฉันจะบอกว่าทำผิดพลาดในด้านการพึ่งพาการเพิ่มประสิทธิภาพคอมไพเลอร์มากเกินไปน้อยเกินไปและคำแนะนำนี้มาจากผู้ชายที่ทำงานในสาขาที่มีประสิทธิภาพสูงซึ่งประสิทธิภาพการบำรุงรักษาและคุณภาพการรับรู้ของลูกค้าคือ ยักษ์เบลอทั้งหมด ฉันอยากให้คุณพึ่งพาเครื่องมือเพิ่มประสิทธิภาพของคุณอย่างมั่นใจและพบว่ามีกรณีที่คลุมเครือซึ่งคุณไว้ใจมากเกินกว่าจะพึ่งน้อยเกินไปและเพิ่งเขียนโค้ดจากความกลัวที่เชื่อโชคลางตลอดเวลาตลอดชีวิตที่เหลือของคุณ อย่างน้อยคุณก็จะเข้าถึงผู้สร้างโปรไฟล์และตรวจสอบอย่างถูกต้องหากสิ่งต่าง ๆ ไม่ได้ดำเนินการอย่างรวดเร็วเท่าที่ควรและได้รับความรู้ที่มีค่าไม่ใช่ความเชื่อทางไสยศาสตร์

คุณทำได้ดีในการพึ่งพาเครื่องมือเพิ่มประสิทธิภาพ ให้ทัน อย่าเป็นเหมือนคนที่เริ่มขออินไลน์อย่างชัดเจนทุกฟังก์ชั่นที่เรียกว่าเป็นลูปก่อนที่จะรวบรวมข้อมูลจากความกลัวที่เข้าใจผิดเกี่ยวกับข้อบกพร่องของเครื่องมือเพิ่มประสิทธิภาพ

โปรไฟล์

การทำโปรไฟล์เป็นวงเวียน แต่เป็นคำตอบที่ดีที่สุดสำหรับคำถามของคุณ ผู้เริ่มต้นปัญหาอยากเขียนโค้ดที่มีประสิทธิภาพบ่อยครั้งที่การต่อสู้ไม่ใช่สิ่งที่จะเพิ่มประสิทธิภาพ แต่เป็นสิ่งที่ไม่ควรปรับปรุงเพราะพวกเขาพัฒนาลางสังหรณ์ที่เข้าใจผิดทุกชนิดเกี่ยวกับความไร้ประสิทธิภาพที่ในขณะที่สัญชาตญาณของมนุษย์ การพัฒนาประสบการณ์กับผู้สร้างโปรไฟล์จะทำให้คุณได้รับการชื่นชมอย่างเหมาะสมไม่เพียง แต่ความสามารถในการเพิ่มประสิทธิภาพของคอมไพเลอร์ซึ่งคุณสามารถพึ่งพาได้อย่างมั่นใจ แต่ยังรวมถึงขีดความสามารถ (รวมถึงข้อ จำกัด ) ของฮาร์ดแวร์ของคุณ มีค่ามากขึ้นในการทำโปรไฟล์ในการเรียนรู้สิ่งที่ไม่คุ้มค่าที่จะเพิ่มประสิทธิภาพมากกว่าการเรียนรู้สิ่งที่เป็น


-1

ซอฟต์แวร์สามารถเขียนใน C ++ บนแพลตฟอร์มที่แตกต่างกันมากและเพื่อวัตถุประสงค์ที่แตกต่างกันมากมาย

มันขึ้นอยู่กับวัตถุประสงค์ของซอฟต์แวร์อย่างสมบูรณ์ มันควรจะง่ายต่อการบำรุงรักษาขยายแก้ไข refactor และอื่น ๆ หรือเป็นสิ่งอื่นที่สำคัญกว่าเช่นประสิทธิภาพราคาหรือความเข้ากันได้กับฮาร์ดแวร์เฉพาะบางอย่างหรือเวลาที่ใช้ในการพัฒนา


-2

ฉันคิดว่าคำตอบที่น่าเบื่อคือ: 'มันขึ้นอยู่กับว่า'

มันคือการปฏิบัติที่ดีที่จะเขียนโค้ดที่อาศัยอยู่กับการเพิ่มประสิทธิภาพของคอมไพเลอร์ที่มีแนวโน้มที่จะถูกปิดและที่ช่องโหว่ที่ไม่ได้รับการบันทึกไว้และสถานที่ที่รหัสในคำถามไม่ได้เป็นหน่วยทดสอบเพื่อที่ว่าถ้ามันไม่ทำลายคุณจะรู้ว่ามัน ? อาจ.

มันเป็นการปฏิบัติที่ไม่ถูกต้องหรือไม่ในการเขียนโค้ดที่ต้องอาศัยการเพิ่มประสิทธิภาพของคอมไพเลอร์ที่ไม่น่าจะถูกปิดนั่นคือการบันทึกและทดสอบหน่วย ? อาจจะไม่.


-6

หากคุณไม่ได้บอกเรามากกว่านี้ก็เป็นการฝึกฝนที่ไม่ดี แต่ไม่ใช่ด้วยเหตุผลที่คุณแนะนำ

อาจแตกต่างจากภาษาอื่น ๆ ที่คุณเคยใช้มาก่อนการคืนค่าของวัตถุใน C ++ ให้ผลลัพธ์เป็นสำเนาของวัตถุ หากคุณปรับเปลี่ยนวัตถุคุณกำลังแก้ไขวัตถุอื่น นั่นคือถ้าผมมีObj a; a.x=1;และObj b = a;ผมก็ทำb.x += 2; b.f();แล้วa.xยังคงเท่ากับ 1, 3 ไม่ได้

ดังนั้นไม่ใช้วัตถุเป็นค่าแทนการอ้างอิงหรือตัวชี้ไม่ได้มีฟังก์ชั่นเดียวกันและคุณอาจท้ายด้วยข้อบกพร่องในซอฟต์แวร์ของคุณ

บางทีคุณอาจรู้และไม่ส่งผลเสียต่อการใช้งานเฉพาะของคุณ อย่างไรก็ตามตามถ้อยคำในคำถามของคุณปรากฏว่าคุณอาจไม่ทราบถึงความแตกต่าง ถ้อยคำเช่น "สร้างวัตถุในฟังก์ชั่น"

"สร้างวัตถุในฟังก์ชั่น" เสียงเหมือนnew Obj;ที่ "ส่งคืนวัตถุตามค่า" เสียงเหมือนObj a; return a;

Obj a;และObj* a = new Obj;แตกต่างกันมาก อดีตอาจส่งผลให้เกิดความเสียหายหน่วยความจำหากไม่ได้ใช้และเข้าใจอย่างถูกต้องและหลังอาจส่งผลให้หน่วยความจำรั่วหากไม่ได้ใช้และเข้าใจอย่างถูกต้อง


8
Return value Optimization (RVO) เป็น semantic ที่กำหนดไว้อย่างดีโดยคอมไพเลอร์สร้างวัตถุที่ส่งคืนหนึ่งระดับขึ้นไปบนสแต็กเฟรมโดยเฉพาะหลีกเลี่ยงการคัดลอกวัตถุที่ไม่จำเป็น นี่คือพฤติกรรมที่กำหนดไว้อย่างดีที่ได้รับการสนับสนุนมานานก่อนที่จะได้รับคำสั่งใน C ++ 17 แม้กระทั่ง 10-15 ปีที่แล้วคอมไพเลอร์รายใหญ่ทั้งหมดสนับสนุนฟีเจอร์นี้และทำอย่างสม่ำเสมอ

@Snowman ฉันไม่ได้พูดถึงการจัดการหน่วยความจำกายภาพระดับต่ำและฉันไม่ได้พูดคุยเกี่ยวกับหน่วยความจำที่ขยายตัวหรือความเร็ว ขณะที่ฉันแสดงในคำตอบของฉันโดยเฉพาะฉันกำลังพูดถึงข้อมูลเชิงตรรกะ เหตุผลการจัดหาค่าของวัตถุคือการสร้างสำเนาของมันโดยไม่คำนึงถึงวิธีการใช้งานคอมไพเลอร์หรือสิ่งที่ใช้ประกอบอยู่เบื้องหลัง สิ่งที่อยู่เบื้องหลังระดับต่ำเป็นสิ่งหนึ่งและโครงสร้างเชิงตรรกะและพฤติกรรมของภาษานั้นเป็นอีกสิ่งหนึ่ง พวกเขาเกี่ยวข้อง แต่ไม่เหมือนกัน - ควรเข้าใจทั้งคู่
แอรอน

6
คำตอบของคุณบอกว่า "คืนค่าของวัตถุใน C ++ ให้ผลตอบแทนสำเนาของวัตถุ" ซึ่งเป็นเท็จอย่างสมบูรณ์ในบริบทของ RVO - วัตถุถูกสร้างขึ้นโดยตรงที่ตำแหน่งการโทรและไม่มีการทำสำเนาเลย คุณสามารถทดสอบได้โดยการลบตัวสร้างสำเนาและส่งคืนวัตถุที่สร้างขึ้นในreturnคำสั่งซึ่งเป็นข้อกำหนดสำหรับ RVO นอกจากนี้คุณยังสามารถพูดคุยเกี่ยวกับคำหลักnewและพอยน์เตอร์ซึ่งไม่ใช่ RVO ที่เกี่ยวกับ ฉันเชื่อว่าคุณไม่เข้าใจคำถามหรือ RVO หรืออาจเป็นไปได้ทั้งสองอย่าง

-7

Pieter B นั้นถูกต้องอย่างยิ่งในการแนะนำสิ่งที่น่าประหลาดใจ

เพื่อตอบคำถามเฉพาะของคุณความหมาย (น่าจะเป็นไปได้มากที่สุด) ในภาษา C ++ คือคุณควรกลับstd::unique_ptrไปหาวัตถุที่สร้างขึ้น

เหตุผลก็คือสิ่งนี้ชัดเจนสำหรับนักพัฒนา C ++ว่าเกิดอะไรขึ้น

แม้ว่าวิธีการของคุณอาจจะใช้งานได้ดีที่สุด แต่คุณกำลังส่งสัญญาณว่าวัตถุนั้นเป็นประเภทค่าเล็ก ๆ ยิ่งไปกว่านั้นคุณยังละทิ้งความเป็นไปได้ใด ๆ สำหรับสิ่งที่เป็นนามธรรมของอินเตอร์เฟส สิ่งนี้อาจใช้ได้สำหรับวัตถุประสงค์ปัจจุบันของคุณ แต่มักจะมีประโยชน์มากเมื่อต้องจัดการกับเมทริกซ์

ฉันซาบซึ้งว่าถ้าคุณมาจากภาษาอื่น ๆ กลุ่มคนทั้งหมดอาจสับสนในขั้นแรก แต่ระวังอย่าคิดเอาเองว่าถ้าคุณไม่ใช้มันคุณก็จะทำให้โค้ดของคุณชัดเจนขึ้น ในทางปฏิบัติตรงกันข้ามมีแนวโน้มที่จะเป็นจริง


เมื่ออยู่ในโรมทำตามที่ชาวโรมันทำ

14
นี่ไม่ใช่คำตอบที่ดีสำหรับประเภทที่ตัวเองไม่ทำการจัดสรรแบบไดนามิก ที่ OP รู้สึกว่าเป็นเรื่องธรรมชาติในกรณีที่ใช้งานของเขาคือการส่งคืนตามค่าบ่งชี้ว่าวัตถุของเขามีระยะเวลาการจัดเก็บอัตโนมัติที่ฝั่งผู้โทร สำหรับวัตถุที่เรียบง่ายและไม่ใหญ่เกินไปแม้แต่การนำไปใช้การคัดลอก - คืน - ค่าที่ไร้เดียงสาจะเป็นลำดับของขนาดที่เร็วกว่าการจัดสรรแบบไดนามิก (หากในทางกลับกันฟังก์ชันจะส่งคืนคอนเทนเนอร์ดังนั้นการส่งค่า unique_pointer อาจเป็นประโยชน์เมื่อเทียบกับคอมไพเลอร์ไร้เดียงสาที่ส่งคืนตามค่า)
Peter A. Schneider

9
@ แมทในกรณีที่คุณไม่ได้ตระหนักว่านี่ไม่ใช่วิธีปฏิบัติที่ดีที่สุด การจัดสรรหน่วยความจำโดยไม่จำเป็นและการบังคับซีแมนทิกส์ชี้บนผู้ใช้นั้นไม่ดี
nwp

5
ประการแรกเมื่อใช้ตัวชี้สมาร์ทหนึ่งควรกลับstd::make_uniqueไม่ใช่std::unique_ptrโดยตรง ประการที่สอง RVO ไม่ใช่การเพิ่มประสิทธิภาพเฉพาะผู้ขายที่เฉพาะเจาะจงลึกลับ: มันถูกอบเข้าสู่มาตรฐาน ถึงแม้จะไม่เป็นเช่นนั้นก็ยังได้รับการสนับสนุนและพฤติกรรมที่คาดหวัง ไม่มีจุดใดที่จะส่งคืน a std::unique_ptrเมื่อไม่จำเป็นต้องใช้ตัวชี้ในตอนแรก

4
@Snowman: ไม่มี "เมื่อมันไม่ได้" แม้ว่าจะเป็นข้อบังคับเมื่อเร็ว ๆ นี้ทุกมาตรฐาน C ++ ที่เคยรู้จัก [N] RVO และทำให้ที่พักเพื่อเปิดใช้งานมัน (เช่นคอมไพเลอร์ได้รับอนุญาตอย่างชัดเจนเสมอเพื่อละเว้นการใช้ตัวสร้างการคัดลอกในค่าส่งคืนแม้ว่าจะ มีผลข้างเคียงที่มองเห็นได้)
Jerry Coffin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.