สำนวน C ++ ใดเลิกใช้ใน C ++ 11


192

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

วิธีการเข้ารหัสแบบเก่า ๆ นั้นด้อยกว่า C ++ 11 สไตล์แน่นอนและตอนนี้เราจะทำอะไรได้บ้าง?

ในการตอบคำถามนี้คุณอาจข้ามสิ่งที่ชัดเจนเช่น "ใช้ตัวแปรอัตโนมัติ"


13
คุณไม่สามารถคัดค้านสำนวนได้
Pubby

6
คำพูดของ Herb Sutterที่ Going Native 2012 กล่าวถึงเรื่องนี้:
bames53

5
ไม่สนับสนุนการส่งคืนค่าคงที่ เห็นได้ชัดว่าauto_ptrเลิกใช้แล้วเช่นกัน
Kerrek SB

27
แน่นอนคุณสามารถ Pubby ก่อนที่จะประดิษฐ์เทมเพลต C ++ มีเทคนิคการทำเทมเพลตมาโคร จากนั้น C ++ ก็เพิ่มเข้ามาและวิธีการเดิมก็ถือว่าไม่ดี
Alan Baljeu

7
คำถามนี้ต้องถูกย้ายไปยัง Programmers.se
Nicol Bolas

คำตอบ:


173
  1. Final Class : C ++ 11 ให้ตัวfinalระบุเพื่อป้องกันการสืบทอดคลาส
  2. C ++ 11 lambdas ลดความจำเป็นในการใช้คลาสฟังก์ชั่นชื่อวัตถุ (functor)
  3. Move Constructor : ไม่ต้องใช้เวทย์มนตร์ในการstd::auto_ptrทำงานอีกต่อไปเนื่องจากการสนับสนุนชั้นหนึ่งสำหรับการอ้างอิง rvalue
  4. บูลที่ปลอดภัย : สิ่งนี้ถูกกล่าวถึงก่อนหน้านี้ ตัวดำเนินการอย่างชัดเจนของ C ++ 11 จะกำจัดสำนวน C ++ 03 ที่พบบ่อยมากนี้
  5. ย่อขนาดให้พอดี : คอนเทนเนอร์ C ++ 11 STL จำนวนมากมีshrink_to_fit()ฟังก์ชันสมาชิกซึ่งควรกำจัดความต้องการในการแลกเปลี่ยนด้วยชั่วคราว
  6. คลาสฐานชั่วคราว : ไลบรารี C ++ เก่าบางตัวใช้สำนวนที่ค่อนข้างซับซ้อน ด้วยซีแมนทิกส์ย้ายไม่จำเป็นอีกต่อไป
  7. Type Enum Enumerations ปลอดภัยมากใน C ++ 11
  8. การห้ามการจัดสรรฮีป : = deleteไวยากรณ์เป็นวิธีที่ตรงกว่าในการบอกว่าการทำงานเฉพาะนั้นถูกปฏิเสธอย่างชัดเจน สิ่งนี้ใช้ได้กับการป้องกันการจัดสรรฮีป (เช่น=deleteสำหรับสมาชิกoperator new) การป้องกันการคัดลอกการมอบหมาย ฯลฯ
  9. เท็มเพลต Templated : เทมเพลต Aliasใน C ++ 11 ลดความต้องการเทมเพลตเท็มเพลตที่เรียบง่าย อย่างไรก็ตามเครื่องกำเนิดไฟฟ้าชนิดซับซ้อนยังคงต้องการฟังก์ชันเมตา
  10. การคำนวณเวลารวบรวมแบบตัวเลขบางตัวเช่นฟีโบนักชีสามารถเปลี่ยนได้อย่างง่ายดายโดยใช้นิพจน์คงที่ทั่วไป
  11. result_of: การใช้แม่แบบชั้นจะถูกแทนที่ด้วยresult_of decltypeฉันคิดว่าresult_ofใช้decltypeเมื่อมันใช้ได้
  12. สมาชิกในคลาส initializersบันทึกการพิมพ์สำหรับการเริ่มต้นเริ่มต้นของสมาชิกแบบคงที่ที่มีค่าเริ่มต้น
  13. ในรหัส C ++ 11 NULLใหม่ควรกำหนดใหม่เป็นnullptrแต่ดูการพูดคุยของ STLเพื่อเรียนรู้ว่าทำไมพวกเขาถึงตัดสินใจ
  14. ผู้ที่ชื่นชอบเทมเพลตของนิพจน์มีความยินดีที่ได้รับไวยากรณ์ของฟังก์ชันประเภทส่งคืนใน C ++ 11 ไม่มีประเภทผลตอบแทนคืนยาว 30 บรรทัด!

ฉันคิดว่าฉันจะหยุดที่นั่น!


ขอบคุณสำหรับรายละเอียด!
Alan Baljeu

7
คำตอบที่ดี แต่ฉันจะประท้วงresult_ofจากรายการ แม้จะมีความยุ่งยากที่typenameจำเป็นก่อนหน้านี้ฉันคิดว่าtypename result_of<F(Args...)::typeบางครั้งสามารถอ่านได้ง่ายกว่าdecltype(std::declval<F>()(std::declval<Args>()...)และด้วยการยอมรับN3436ลงในกระดาษทำงานที่ทั้งคู่ทำงานกับ SFINAE (ซึ่งเคยเป็นข้อได้เปรียบdecltypeที่result_ofไม่ได้นำเสนอ)
Jonathan Wakely

เกี่ยวกับ 14) ฉันยังคงร้องไห้ว่าฉันต้องใช้มาโครเพื่อเขียนโค้ดเดียวกันสองครั้ง - หนึ่งครั้งสำหรับส่วนของฟังก์ชั่นและอีกครั้งสำหรับคำสั่ง decltype () ...

2
ฉันต้องการทราบว่าหัวข้อนี้เชื่อมโยงจากหน้า Microsoft นี้เป็นบทความ "สำหรับข้อมูลเพิ่มเติม" ในการแนะนำทั่วไปเกี่ยวกับภาษา C ++ แต่หัวข้อนี้เป็นหัวข้อที่มีความเชี่ยวชาญสูง! ฉันขอแนะนำสั้น ๆว่า "หัวข้อนี้ไม่เหมาะสำหรับสามเณร C ++!" คำแนะนำจะรวมอยู่ที่จุดเริ่มต้นของหัวข้อหรือคำตอบนี้
Aacini

Re 12: "การเริ่มต้นสมาชิกในคลาส" - นั่นคือสำนวนใหม่ไม่ใช่สำนวนที่เลิกใช้แล้วใช่ไหม สลับลำดับประโยคบางที Re 2: Functors มีประโยชน์มากเมื่อคุณต้องการที่จะส่งผ่านชนิดต่างๆมากกว่าวัตถุ (โดยเฉพาะในพารามิเตอร์แม่แบบ) จึงเป็นเพียงบางส่วนใช้ functors ที่มีการเลิกใช้
einpoklum

66

ในช่วงเวลาหนึ่งมันก็เป็นที่ถกเถียงกันอยู่ว่าควรจะกลับมาตามconstตัวอักษรแทนที่จะตามตัวอักษรตามตัวอักษร:

const A foo();
^^^^^

สิ่งนี้ไม่เป็นอันตรายส่วนใหญ่ใน C ++ 98/03 และอาจพบข้อบกพร่องบางอย่างที่ดูเหมือน:

foo() = a;

แต่การย้อนกลับโดยconstมีข้อห้ามใน C ++ 11 เพราะยับยั้งการย้ายความหมาย:

A a = foo();  // foo will copy into a instead of move into it

ดังนั้นเพียงแค่ผ่อนคลายและรหัส:

A foo();  // return by non-const value

9
ความผิดพลาดที่สามารถป้องกันได้ในขณะนี้สามารถถูกจับได้โดยใช้ตัวระบุคุณสมบัติอ้างอิงสำหรับฟังก์ชัน เช่นในกรณีดังกล่าวข้างต้นกำหนดแทนA& operator=(A o)& A& operator=(A o)สิ่งเหล่านี้จะป้องกันความผิดพลาดที่โง่เขลาและทำให้คลาสมีพฤติกรรมเหมือนประเภทพื้นฐานและไม่ป้องกันความหมายของการย้าย
Joe

61

เร็วที่สุดเท่าที่คุณสามารถละทิ้ง0และNULLในความโปรดปรานของnullptrทำเช่นนั้น!

ในรหัสที่ไม่ใช่แบบทั่วไปการใช้0หรือNULLไม่ได้เป็นเรื่องใหญ่ แต่ทันทีที่คุณเริ่มส่งผ่านค่าคงที่ตัวชี้โมฆะในรหัสทั่วไปสถานการณ์จะเปลี่ยนไปอย่างรวดเร็ว เมื่อคุณผ่าน0การtemplate<class T> func(T) Tรับการอนุมานเป็นintและไม่เป็นค่าคงที่ตัวชี้โมฆะ และไม่สามารถแปลงกลับเป็นค่าคงที่ตัวชี้ว่างหลังจากนั้น nullptrน้ำตกนี้ลงในหล่มปัญหาที่ก็ไม่อยู่ถ้าจักรวาลใช้เฉพาะ

C ++ 11 ไม่คัดค้าน0และNULLเป็นค่าคงที่ตัวชี้โมฆะ แต่คุณควรเขียนโค้ดราวกับว่ามันเป็น


เดคไทป์ (nullptr) คืออะไร?

4
@GrapschKnutsch: std::nullptr_tมันเป็น
Howard Hinnant

แนะนำสิ่งนี้ให้ใช้ถ้อยคำใหม่เพราะสำนวนเลิกใช้มากกว่าการประชุมใหม่ที่จะนำมาใช้ (เช่น "การใช้0หรือNULLสำหรับตัวชี้โมฆะ")
einpoklum

38

ปลอดภัยสำนวนบูลexplicit operator bool()

ตัวสร้างสำเนาส่วนตัว (boost :: noncopyable) → X(const X&) = delete

การจำลองคลาสสุดท้ายด้วย destructor ส่วนตัวและการสืบทอดเสมือนจริงclass X final


ตัวอย่างที่ดีและรัดกุมซึ่งหนึ่งในนั้นก็ถือคำว่า "สำนวน" ในนั้น ใส่อย่างดี
เซบาสเตียนมัค

2
ว้าวฉันไม่เคยเห็น 'safe bool idiom' มาก่อนมันน่าขยะแขยงทีเดียว! ฉันหวังว่าฉันไม่ต้องการมันในโค้ด pre-C ++ 11 ...
boycy

24

หนึ่งในสิ่งที่ทำให้คุณหลีกเลี่ยงการเขียนอัลกอริธึมพื้นฐานใน C ++ 11 คือความพร้อมใช้ของ lambdas เมื่อใช้ร่วมกับอัลกอริทึมที่จัดทำโดยไลบรารีมาตรฐาน

ฉันกำลังใช้สิ่งเหล่านี้ในตอนนี้และมันเหลือเชื่อว่าคุณจะบอกสิ่งที่คุณต้องการทำบ่อยเพียงใดโดยใช้ count_if (), for_each () หรืออัลกอริทึมอื่น ๆ แทนที่จะต้องเขียนลูปแช่งอีกครั้ง

เมื่อคุณใช้คอมไพเลอร์ C ++ 11 กับไลบรารี่มาตรฐาน C ++ 11 ที่สมบูรณ์คุณจะไม่มีข้อแก้ตัวที่ดีอีกต่อไปที่จะไม่ใช้อัลกอริธึมมาตรฐานเพื่อสร้างของคุณ แลมบ์ดาแค่ฆ่ามัน

ทำไม?

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

โดยทั่วไปขั้นตอนวิธีการอ่านที่สร้างด้วยอัลกอริธึมมาตรฐานนั้นง่ายกว่ามากเมื่อคำที่ซ่อนรายละเอียดการนำไปใช้ของลูป

ฉันเดาว่าอัลกอริทึมระดับสูงกว่าต้องได้รับการคิดตอนนี้ว่าเรามีอัลกอริทึมระดับต่ำกว่าที่จะสร้าง


8
จริงๆแล้วมีข้อแก้ตัวที่ดี คุณกำลังใช้อัลกอริธึมของ Boost.Rangeซึ่งดีกว่าเยอะมาก)
Nicol Bolas

10
ฉันไม่เห็นว่าfor_eachด้วยแลมบ์ดานั้นดีไปกว่าช่วงที่อิงตามลูปด้วยเนื้อหาของแลมบ์ดาในลูป รหัสมีลักษณะเหมือนกันมากกว่าหรือน้อยกว่า แต่แลมบ์ดาแนะนำเครื่องหมายวรรคตอนพิเศษบางอย่าง คุณสามารถใช้สิ่งที่เทียบเท่าboost::irangeเพื่อนำไปใช้กับลูปมากกว่าที่ใช้ตัววนซ้ำอย่างเห็นได้ชัด นอกจากนี้ยังขึ้นอยู่กับช่วงของลูปที่มีความยืดหยุ่นมากกว่าซึ่งคุณสามารถออกก่อนกำหนดได้หากต้องการ (โดยreturnหรือตามbreak) ในขณะที่for_eachคุณต้องโยน
Steve Jessop

5
@SteveJessop: ถึงกระนั้นความพร้อมใช้งานของช่วงที่ใช้forทำให้it = c.begin(), const end = c.end(); it != end; ++itสำนวนปกติหมดอายุ
Ben Voigt

7
@SteveJessop ข้อดีอย่างหนึ่งของfor_eachอัลกอริทึมในช่วงตามห่วงคือการที่คุณไม่สามารถ หรือbreak returnนั่นคือเมื่อคุณเห็นfor_eachคุณรู้ทันทีโดยไม่ต้องดูร่างกายว่าไม่มีความยุ่งยากดังกล่าว
bames53

5
@Klaim: จะเฉพาะเจาะจงผมเปรียบเทียบตัวอย่างด้วยstd::for_each(v.begin(), v.end(), [](int &i) { ++i; }); for (auto &i : v) { ++i; }ฉันยอมรับว่าความยืดหยุ่นนั้นเป็นแบบสองด้าน ( gotoมีความยืดหยุ่นมากนั่นเป็นปัญหา) ฉันไม่คิดว่าข้อ จำกัด ที่จะไม่สามารถที่จะใช้breakในfor_eachชดเชยรุ่นสำหรับฟุ่มเฟื่อยเสริมความต้องการ - ผู้ใช้ของfor_eachที่นี่จะ IMO เสียสละให้สามารถอ่านได้ที่เกิดขึ้นจริงและความสะดวกสบายสำหรับชนิดของความคิดทฤษฎีที่ได้for_eachคือในหลักการที่ชัดเจนและแนวคิด ที่เรียบง่าย ในทางปฏิบัติมันไม่ชัดเจนหรือง่ายขึ้น
Steve Jessop

10

คุณจะต้องใช้งานเวอร์ชันที่กำหนดเองswapไม่บ่อยนัก ใน C ++ 03 การโยนแบบไม่มีประสิทธิภาพswapมักจะจำเป็นเพื่อหลีกเลี่ยงค่าใช้จ่ายและการคัดลอกและเนื่องจากต้องstd::swapใช้สองสำเนาswapบ่อยครั้งจะต้องมีการปรับแต่ง ใน C ++ ให้std::swapใช้moveและเพื่อให้ความสำคัญกับการใช้งานตัวสร้างการย้ายที่มีประสิทธิภาพและไม่โยนและตัวดำเนินการกำหนดค่า เนื่องจากสำหรับค่าเริ่มต้นเหล่านี้มักจะใช้ได้ผลนี้จะทำงานน้อยกว่าใน C ++ 03

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


1
ฉันสงสัยเรื่องนี้ รูปแบบที่แนะนำคือการใช้ swap เพื่อย้ายและคัดลอกโครงสร้าง แต่ไม่ใช่ std :: swap เพราะนั่นจะเป็นแบบวงกลม
Alan Baljeu

ใช่ แต่ตัวสร้างการย้ายมักจะเรียกว่าการแลกเปลี่ยนแบบกำหนดเองหรือเทียบเท่า
Inverse

2

ฉันไม่ทราบชื่อของมัน แต่รหัส C ++ 03 มักจะใช้โครงสร้างต่อไปนี้แทนการมอบหมายการย้ายที่ขาดหายไป:

std::map<Big, Bigger> createBigMap(); // returns by value

void example ()
{
  std::map<Big, Bigger> map;

  // ... some code using map

  createBigMap().swap(map);  // cheap swap
}

สิ่งนี้หลีกเลี่ยงการทำสำเนาใด ๆ เนื่องจากการคัดลอกตัวเลือกรวมกับswapด้านบน


1
ในตัวอย่างของคุณการสลับไม่จำเป็นคัดลอกตัวเลือกจะสร้างค่าตอบแทนในmapอย่างไรก็ตาม เทคนิคที่คุณแสดงมีประโยชน์ถ้าmapมีอยู่แล้วแทนที่จะสร้างขึ้นใหม่ ตัวอย่างน่าจะดีกว่าหากไม่มีความคิดเห็น "ตัวสร้างค่าเริ่มต้นราคาถูก" และด้วย "// ... " ระหว่างการก่อสร้างนั้นกับการแลกเปลี่ยน
Jonathan Wakely

ฉันเปลี่ยนมันตามคำแนะนำของคุณ ขอบคุณ
Andrzej

การใช้ "ใหญ่" และ "ใหญ่กว่า" ทำให้เกิดความสับสน ทำไมไม่อธิบายว่าขนาดของคีย์และประเภทของค่ามีความสำคัญอย่างไร
einpoklum

1

เมื่อฉันสังเกตเห็นว่าคอมไพเลอร์ใช้มาตรฐาน C ++ 11 ไม่ผิดพลาดรหัสต่อไปนี้:

std::vector<std::vector<int>> a;

>> ฉันเริ่มเต้นแล้ว ในรุ่นก่อนหน้าหนึ่งจะต้องทำ

std::vector<std::vector<int> > a;

ในการทำให้เรื่องแย่ลงถ้าคุณต้องแก้ไขจุดบกพร่องนี้คุณจะรู้ว่าข้อความแสดงข้อผิดพลาดที่น่าสยดสยองมาจากเรื่องนี้ได้อย่างไร

อย่างไรก็ตามฉันไม่ทราบว่านี่เป็น "ชัดเจน" สำหรับคุณหรือไม่


1
สถานที่นี้ถูกเพิ่มไปแล้วใน C ++ ก่อนหน้านี้ หรืออย่างน้อย Visual C ++ จะนำไปใช้ตามการอภิปรายมาตรฐานเมื่อหลายปีก่อน
Alan Baljeu

1
@AlanBaljeu แน่นอนมีหลายสิ่งที่ไม่ได้มาตรฐานที่ถูกเพิ่มเข้าไปในคอมไพเลอร์ / ห้องสมุด มีคอมไพเลอร์จำนวนมากที่มีการประกาศตัวแปร "อัตโนมัติ" ก่อน C ++ 11 แต่คุณไม่สามารถแน่ใจได้ว่ารหัสของคุณสามารถรวบรวมได้โดยสิ่งอื่น คำถามคือเกี่ยวกับมาตรฐานไม่ใช่เกี่ยวกับ "มีคอมไพเลอร์ใด ๆ ที่สามารถทำได้"
v010dya

1

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


... แต่สำนวนใดที่เลิกใช้แล้ว
einpoklum

ไม่ใช่สำนวน แต่เป็นการฝึกฝนที่ดีซึ่งไม่จำเป็นอีกต่อไป แม้จะมีคอมไพเลอร์รองรับ RVO ซึ่งเป็นทางเลือก en.wikipedia.org/wiki/Return_value_optimization "ในช่วงแรกของการวิวัฒนาการของ C ++ การไม่สามารถใช้ภาษาในการคืนออบเจ็กต์ประเภทคลาสจากฟังก์ชันได้อย่างมีประสิทธิภาพถือว่าเป็นจุดอ่อน ..... " struct Data {char bytes [ 16]; }; เป็นโมฆะ f (Data * p) {// สร้างผลลัพธ์โดยตรงใน * p} int main () {Data d; f (& D); }
Martin

ฉันกำลังพูดเป็นนัยคุณควรวลีคำตอบของคุณเป็น "ประเพณีของการหลีกเลี่ยงผลตอบแทนตามมูลค่าไม่เกี่ยวข้องเช่น ฯลฯ ฯลฯ "
einpoklum
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.