อัตโนมัติทำให้รหัส C ++ เข้าใจยากขึ้นหรือไม่


122

ผมเห็นการประชุมโดย Sutter สมุนไพรที่เขาสนับสนุนให้ทุก C ++ Programmer autoใช้

ฉันต้องอ่านรหัส C # เมื่อหลายปีก่อนที่varมีการใช้อย่างกว้างขวางและรหัสนั้นยากที่จะเข้าใจ - ทุกครั้งที่varมีการใช้ฉันต้องตรวจสอบประเภทการคืนของด้านขวา บางครั้งมากกว่าหนึ่งครั้งเพราะฉันลืมประเภทของตัวแปรหลังจากที่ในขณะ!

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

ฉันก็รู้ว่าเขียนง่ายกว่า:

auto x = GetX();

กว่า:

someWeirdTemplate<someOtherVeryLongNameType, ...>::someOtherLongType x = GetX();

แต่จะเขียนเพียงครั้งเดียวและGetX()มีการตรวจสอบประเภทส่งคืนหลาย ๆ ครั้งเพื่อทำความเข้าใจว่าxมีประเภทใด

นี่ทำให้ฉันสงสัย - autoทำให้รหัส C ++ ยากที่จะเข้าใจหรือไม่


29
คุณต้องการตรวจสอบผลตอบแทนทุกครั้งหรือไม่? ทำไมประเภทไม่ชัดเจนจากรหัส autoมักจะทำให้สิ่งที่ยากต่อการอ่านเมื่ออ่านยากแล้วคือฟังก์ชั่นยาวเกินไปตัวแปรที่มีชื่อไม่ดี ฯลฯ ในฟังก์ชั่นสั้น ๆ ที่มีตัวแปรที่ตั้งชื่ออย่างเหมาะสมการรู้ประเภทควรเป็นหนึ่งใน # 1 ง่ายหรือ # 2 ไม่เกี่ยวข้อง
R. Martinho Fernandes

25
ว่า "ศิลปะ" ของการใช้เป็นจำนวนมากเช่นการกำหนดเวลาที่จะใช้auto typedefมันขึ้นอยู่กับคุณที่จะกำหนดว่ามันจะเป็นอุปสรรคเมื่อไหร่และเมื่อไหร่จะช่วย
ahenderson

18
ฉันคิดว่าฉันมีปัญหาเดียวกัน แต่จากนั้นฉันก็ตระหนักว่าฉันสามารถเข้าใจโค้ดโดยไม่ทราบประเภท เช่น: "auto idx = get_index ();" ดังนั้น idx จึงเป็นสิ่งที่ถือดัชนี ประเภทที่แน่นอนคือค่อนข้างไม่เกี่ยวข้องกับกรณีส่วนใหญ่
PlasmaHH

31
ดังนั้นอย่าเขียนauto x = GetX();เลือกชื่อที่ดีกว่าxที่จริงจะบอกคุณว่ามันทำอะไรในบริบทเฉพาะ ... นั่นมักจะมีประโยชน์มากกว่าประเภทของมันอยู่แล้ว
โจนาธาน Wakely

11
หากใช้การอนุมานประเภทมากขึ้นจะทำให้โปรแกรมเมอร์อ่านโค้ดไม่ได้รหัสหรือโปรแกรมเมอร์ต้องการการปรับปรุงที่จริงจัง
CA McCann

คำตอบ:


99

คำตอบสั้น ๆ : สมบูรณ์ยิ่งขึ้นความเห็นปัจจุบันของฉันautoคือคุณควรใช้เป็นautoค่าเริ่มต้นเว้นแต่คุณต้องการแปลงอย่างชัดเจน (แม่นยำยิ่งขึ้นเล็กน้อย "... เว้นแต่คุณต้องการกำหนดประเภทให้ชัดเจนซึ่งเกือบจะเป็นเพราะคุณต้องการให้เกิด Conversion")

คำตอบและเหตุผลอีกต่อไป:

เขียนประเภทที่ชัดเจน (แทนauto) เฉพาะเมื่อคุณต้องการที่จะยอมรับประเภทอย่างชัดเจนเท่านั้นซึ่งเกือบจะหมายความว่าคุณต้องการรับการแปลงประเภทนั้นอย่างชัดเจน จากด้านบนของหัวของฉันฉันจำได้สองกรณีหลัก:

  • (ทั่วไป) เดอะinitializer_listแปลกใจที่ฉงนฉงายauto x = { 1 }; initializer_listถ้าคุณไม่ต้องการinitializer_listพูดประเภท - เช่นขอแปลงอย่างชัดเจน
  • (หายาก) เคสเทมเพลตนิพจน์เช่นที่auto x = matrix1 * matrix 2 + matrix3;จับชนิดตัวช่วยหรือพร็อกซีไม่ได้หมายถึงโปรแกรมเมอร์ ในหลายกรณีมันเป็นเรื่องดีและใจดีที่จะจับภาพประเภทนั้น แต่บางครั้งถ้าคุณต้องการให้มันยุบตัวและทำการคำนวณแล้วพูดประเภทนั้น - นั่นคือขอการแปลงอีกครั้งอย่างชัดเจน

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

  • ความถูกต้อง:การใช้การautoรับรองคุณจะได้ประเภทที่ถูกต้อง ตามที่พูดไปถ้าคุณทำซ้ำตัวเอง (พูดประเภทซ้ำซ้อน) คุณสามารถและจะโกหก (เข้าใจผิด) นี่คือตัวอย่างปกติ: void f( const vector<int>& v ) { for( /*…*- ณ จุดนี้ถ้าคุณเขียนชนิดตัววนซ้ำอย่างชัดเจนคุณต้องการจำที่จะเขียนconst_iterator(ใช่ไหม) ในขณะที่autoเพิ่งทำให้ถูกต้อง
  • การบำรุงรักษาและความทนทาน:การใช้autoทำให้โค้ดของคุณแข็งแกร่งขึ้นเมื่อเผชิญกับการเปลี่ยนแปลงเนื่องจากเมื่อการเปลี่ยนแปลงประเภทของนิพจน์autoจะดำเนินการแก้ไขประเภทที่ถูกต้องต่อไป หากคุณมุ่งมั่นที่จะเป็นประเภทที่ชัดเจนการเปลี่ยนประเภทของการแสดงออกจะฉีดการแปลงเงียบเมื่อประเภทใหม่แปลงเป็นประเภทเก่าหรือแบ่งสร้างที่ไม่มีความจำเป็นเมื่อประเภทใหม่ยังคงทำงานเหมือนประเภทเก่า แต่ไม่แปลงเป็นเก่า ประเภท (เช่นเมื่อคุณเปลี่ยนmapไปยังunordered_mapซึ่งมักจะดีถ้าคุณไม่ได้อาศัยเพื่อใช้autoสำหรับ iterators คุณได้อย่างลงตัวจะเปลี่ยนจากmap<>::iteratorการunordered_map<>::iteratorแต่การใช้map<>::iterator ทุกที่อย่างชัดเจนหมายความว่าคุณจะเสียเวลาอันมีค่าของคุณไปกับระลอกแก้ไขรหัสเชิงกลยกเว้นว่าผู้ฝึกหัดกำลังเดินผ่านและคุณสามารถกำจัดงานที่น่าเบื่อเหล่านั้น)
  • ประสิทธิภาพการทำงาน:เนื่องจากautoรับประกันว่าจะไม่มีการแปลงโดยนัยซึ่งจะรับประกันประสิทธิภาพที่ดีขึ้นตามค่าเริ่มต้น หากคุณพูดประเภทนั้นและต้องการการแปลงคุณจะได้รับการแปลงโดยไม่แจ้งว่าคุณคาดหวังหรือไม่
  • การใช้งาน:ใช้autoเป็นตัวเลือกที่ดีของคุณเพียงชนิดที่ยากต่อการสะกดและพูดไม่ได้เช่น lambdas และผู้ช่วยแม่แบบสั้น ๆ ของการหันไปซ้ำdecltypeแสดงออกหรือ indirections std::functionมีประสิทธิภาพน้อยกว่าเช่น
  • ความสะดวกสบาย:ใช่autoพิมพ์น้อยลง ฉันพูดถึงความสมบูรณ์ครั้งสุดท้ายเพราะเป็นเหตุผลทั่วไปที่ชอบ แต่ไม่ใช่เหตุผลที่ดีที่สุดที่จะใช้

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

ใช่มี (ตอนนี้) มีGotWเกี่ยวกับเรื่องนี้


14
ฉันพบว่ามีประโยชน์โดยอัตโนมัติแม้ในขณะที่ฉันต้องการแปลง auto x = static_cast<X>(y)มันช่วยให้ฉันไปอย่างชัดเจนขอแปลงโดยไม่ต้องทำซ้ำประเภท: static_castทำให้มันชัดเจนว่าการแปลงที่เป็นเกี่ยวกับวัตถุประสงค์และหลีกเลี่ยงคำเตือนคอมไพเลอร์เกี่ยวกับการแปลง ปกติหลีกเลี่ยงคำเตือนคอมไพเลอร์จะไม่ดีมาก static_castแต่ฉันตกลงกับไม่ได้รับการแจ้งเตือนเกี่ยวกับการแปลงที่ผมพิจารณาอย่างรอบคอบเมื่อผมเขียนเป็น แม้ว่าฉันจะไม่ทำเช่นนี้หากไม่มีคำเตือนในตอนนี้ แต่ฉันต้องการได้รับคำเตือนในอนาคตหากประเภทเปลี่ยนไปในทางที่อาจเป็นอันตราย
Bjarke Hammersholt Roune

6
สิ่งหนึ่งที่ฉันพบautoคือเราควรพยายามตั้งโปรแกรมกับอินเทอร์เฟซ (ไม่ใช่ในแง่ของ OOP) ไม่ใช่กับการใช้งานเฉพาะ มันเหมือนกับเทมเพลตจริงๆ คุณบ่นเกี่ยวกับ "รหัสที่อ่านยาก" หรือไม่เพราะคุณมีพารามิเตอร์ประเภทเทมเพลตTที่ใช้อยู่ทั่วไป? ไม่ฉันไม่คิดอย่างนั้น ในเทมเพลตเราก็ใช้รหัสกับอินเทอร์เฟซการพิมพ์เวลาเป็ดเป็นสิ่งที่หลาย ๆ คนเรียกกัน
Xeo

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

ฉันแปลกใจจริง ๆ ที่ไม่มีใครสนใจ IDEs ... แม้แต่ IDE สมัยใหม่ก็ไม่สนับสนุนการข้ามไปยังคลาส / คำจำกัดความของคลาสในกรณีที่มีautoตัวแปร แต่เกือบทั้งหมดทำอย่างถูกต้องด้วยข้อกำหนดชนิดที่ชัดเจน ไม่มีใครใช้ IDE ทุกคนใช้เฉพาะตัวแปร int / float / bool หรือไม่ ทุกคนต้องการเอกสารภายนอกสำหรับห้องสมุดแทนที่จะเป็นส่วนหัวที่มีเอกสารหรือไม่?
avtomaton

ที่ GotW: herbalutter.com/2013/08/12/…ฉันไม่เห็นว่า "initializer_list เซอร์ไพรส์" เป็นเซอร์ไพรส การจัดฟันรอบ=RHS ไม่สมเหตุสมผลนักโดยการตีความอื่น ๆ (แถบค้ำยัน init-list แต่คุณจำเป็นต้องรู้ว่าคุณกำลังเริ่มต้นautoอะไร หนึ่งที่เป็นที่น่าแปลกใจเป็นauto i{1}ยัง deducing initializer_listแม้จะอ้างว่าไม่ ใช้เวลานี้ยัน init รายการแต่ใช้สำนวนนี้และใช้ประเภทของ ... แต่เราได้รับinitializer_listมีมากเกินไป โชคดีที่ C ++ 17 สามารถแก้ไขทั้งหมดนี้ได้ดี
underscore_d

112

มันเป็นสถานการณ์เป็นกรณี ๆ ไป

บางครั้งมันทำให้โค้ดยากที่จะเข้าใจบางครั้งไม่ ยกตัวอย่างเช่น

void foo(const std::map<int, std::string>& x)
{
   for ( auto it = x.begin() ; it != x.end() ; it++ )
   { 
       //....
   }
}

ง่ายต่อการเข้าใจและเขียนได้ง่ายกว่าการประกาศตัววนซ้ำจริง ๆ

ฉันใช้ C ++ มาระยะหนึ่งแล้ว แต่ฉันสามารถรับประกันได้ว่าฉันจะได้รับข้อผิดพลาดของคอมไพเลอร์ในนัดแรกของฉันเพราะฉันลืมเรื่องนี้ไปconst_iteratorแล้วและจะเริ่มiterator... :)

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


45
เผง ใครเป็นคนดูแลเกี่ยวกับประเภท มันเป็นตัววนซ้ำ ฉันไม่สนใจประเภทสิ่งที่ฉันต้องรู้ก็คือฉันสามารถใช้มันเพื่อย้ำ
R. Martinho Fernandes

5
+1 แม้ว่าคุณจะตั้งชื่อประเภทนั้น แต่คุณก็ตั้งชื่อเป็นstd::map<int, std::string>::const_iteratorดังนั้นจึงไม่ใช่ว่าชื่อจะบอกคุณเกี่ยวกับประเภทนั้นมาก
Steve Jessop

4
@SteveJessop: มันบอกฉันสองสิ่งอย่างน้อยเป็นกุญแจสำคัญและมีค่าเป็นint std::string:)
นาวาซ

16
@Nawaz: และคุณไม่สามารถกำหนดได้it->secondเนื่องจากเป็นตัววนซ้ำ const const std::map<int, std::string>& xซึ่งทั้งหมดเป็นข้อมูลซ้ำของสิ่งที่อยู่ในสายที่ผ่านมา การบอกสิ่งต่าง ๆ หลายครั้งทำให้แจ้งได้ดีขึ้นเป็นครั้งคราว แต่ก็ไม่เป็นเช่นนั้น:
Steve Jessop

11
TBH ฉันไม่ต้องการที่จะทำให้มันมากขึ้นอย่างเห็นได้ชัดเราเพียงแค่ทำซ้ำมากกว่าfor (anX : x) xกรณีปกติที่คุณต้องมีตัววนซ้ำคือเมื่อคุณแก้ไขคอนเทนเนอร์ แต่xเป็นconst&
MSalters

94

ดูอีกวิธีหนึ่ง คุณเขียน:

std::cout << (foo() + bar()) << "\n";

หรือ:

// it is important to know the types of these values
int f = foo();
size_t b = bar();
size_t total = f + b;

std::cout << total << "\n";

บางครั้งมันไม่ช่วยในการสะกดประเภทออกอย่างชัดเจน

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

บางครั้งการทำให้ประเภทชัดเจนอาจมีประโยชน์:

// seems legit    
if (foo() < bar()) { ... }

เมื่อเทียบกับ

// ah, there's something tricky going on here, a mixed comparison
if ((unsigned int)foo() < bar()) { ... }

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

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

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


21
+1 autoช่วยให้คุณสร้างตัวแปรที่กำหนดชื่อด้วยประเภทที่ไม่ระบุชื่อหรือไม่น่าสนใจ ชื่อที่มีความหมายมีประโยชน์
Mankarse

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

27

IMO คุณกำลังมองย้อนกลับนี้ค่อนข้างมาก

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

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

autoหากมีปัญหาที่นี่ก็ไม่ได้ด้วย มันเป็นรหัสที่เหลือและมีโอกาสค่อนข้างดีที่ประเภทที่ชัดเจนนั้นเพียงพอที่จะช่วยเหลือคุณจากการมองเห็นและ / หรือแก้ไขปัญหาหลัก เมื่อคุณแก้ไขปัญหาจริงแล้วความสามารถในการอ่านโค้ดโดยautoทั่วไปจะไม่เป็นไร

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

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


17

มีสาเหตุหลายประการที่ฉันไม่ชอบรถยนต์สำหรับการใช้งานทั่วไป:

  1. คุณสามารถ refactor code โดยไม่ต้องแก้ไขมัน ใช่นี่คือสิ่งหนึ่งที่มักระบุว่าเป็นประโยชน์ของการใช้งานอัตโนมัติ เพียงแค่เปลี่ยนประเภทการคืนของฟังก์ชั่นและหากรหัสทั้งหมดที่เรียกมันว่าใช้อัตโนมัติไม่ต้องใช้ความพยายามเพิ่มเติม! คุณกดคอมไพล์มันสร้าง - 0 คำเตือน, 0 ข้อผิดพลาด - และคุณเพียงแค่ไปข้างหน้าและตรวจสอบรหัสของคุณโดยไม่ต้องจัดการกับความยุ่งเหยิงของการมองผ่านและอาจแก้ไข 80 สถานที่ใช้ฟังก์ชั่น

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

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

auto bThreadOK = CheckThreadHealth ();

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

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

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

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

  1. การพิมพ์รหัสเมื่อเริ่มต้นการเขียนไม่ใช่ส่วนที่เสียเวลามากที่สุดในการเขียนโปรแกรม ใช่อัตโนมัติทำให้การเขียนโค้ดเร็วขึ้นในตอนแรก ตามข้อจำกัดความรับผิดชอบฉันพิมพ์> 100 WPM ดังนั้นอาจไม่รบกวนฉันมากเท่ากับคนอื่น ๆ แต่ถ้าสิ่งที่ฉันต้องทำคือเขียนโค้ดใหม่ทุกวันฉันจะเป็นผู้ไปพักแรมที่มีความสุข ส่วนที่ใช้เวลาส่วนใหญ่ในการเขียนโปรแกรมคือการวินิจฉัยข้อผิดพลาดที่เกิดซ้ำขอบโค้ดในโค้ดซึ่งมักเกิดจากปัญหาที่ไม่ชัดเจนที่เห็นได้ชัดเช่นการใช้งานรถยนต์มากเกินไป (อ้างอิงกับสำเนา) เซ็นชื่อกับไม่ได้ลงนามลอยเทียบกับ int บูลกับตัวชี้ ฯลฯ )

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

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

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


4
คุณตอกมัน หวังว่าฉันจะให้ +2 นี้
cmaster

ที่ดี "ต้องเคราะห์ร้ายระมัดระวัง" - คำตอบ @cmaster: มี
Deduplicator

ฉันพบกรณีที่มีประโยชน์มากกว่าหนึ่งกรณี: auto something = std::make_shared<TypeWithLongName<SomeParam>>(a,b,c);. :-)
Notinlist

14

autoใช่มันทำให้ง่ายต่อการทราบชนิดของตัวแปรของคุณหากคุณไม่ได้ใช้ คำถามคือ: คุณจำเป็นต้องรู้ประเภทของตัวแปรของคุณเพื่ออ่านรหัสหรือไม่? บางครั้งคำตอบคือใช่บางครั้งไม่ ตัวอย่างเช่นเมื่อได้รับ iterator จากstd::vector<int>คุณจะต้องรู้ว่ามันเป็นstd::vector<int>::iteratorหรือจะauto iterator = ...;พอเพียง? ทุกสิ่งที่ใคร ๆ ก็อยากทำกับ iterator นั้นได้รับจากข้อเท็จจริงที่ว่ามันเป็น iterator - มันไม่สำคัญว่ามันจะเป็นแบบไหน

ใช้autoในสถานการณ์เหล่านั้นเมื่อมันไม่ทำให้โค้ดของคุณอ่านยากขึ้น


12

ส่วนตัวผมใช้autoเฉพาะเมื่อมันชัดเจนสำหรับโปรแกรมเมอร์ว่ามันคืออะไร

ตัวอย่างที่ 1

std::map <KeyClass, ValueClass> m;
// ...
auto I = m.find (something); // OK, find returns an iterator, everyone knows that

ตัวอย่างที่ 2

MyClass myObj;
auto ret = myObj.FindRecord (something)// NOT OK, everyone needs to go and check what FindRecord returns

5
นี่เป็นตัวอย่างที่ชัดเจนของการตั้งชื่อไม่ดีที่ทำร้ายความสามารถในการอ่านไม่ใช่เรื่องอัตโนมัติ ไม่มีใครมีความคิดที่เบาที่สุดในสิ่งที่ "DoSomethingWeird" ทำดังนั้นการใช้งานอัตโนมัติหรือไม่ทำให้อ่านไม่ออก คุณจะต้องตรวจสอบเอกสารด้วยวิธีใดวิธีหนึ่ง
R. Martinho Fernandes

4
ตกลงตอนนี้มันค่อนข้างดีกว่า ฉันยังพบว่าตัวแปรนั้นมีชื่อไม่ดีแม้ว่าซึ่งยังคงเจ็บอยู่ หากคุณเขียนauto record = myObj.FindRecord(something)มันจะชัดเจนว่าตัวแปรประเภทนั้นถูกบันทึกไว้ หรือตั้งชื่อitหรือคล้ายกันจะทำให้ชัดเจนว่ามันส่งกลับ iterator โปรดทราบว่าแม้ว่าคุณจะไม่ได้ใช้autoอย่างถูกต้องตั้งชื่อตัวแปรจะหมายความว่าคุณไม่จำเป็นต้องกระโดดกลับไปยังประกาศจะมองไปที่ชนิดจากทุกที่ในฟังก์ชั่น ฉันลบ downvote ของฉันเพราะตอนนี้ตัวอย่างไม่ได้เป็น strawman ที่สมบูรณ์ แต่ฉันยังไม่ได้ซื้ออาร์กิวเมนต์ที่นี่
R. Martinho Fernandes

2
หากต้องการเพิ่มไปยัง @ R.MartinhoFernandes: คำถามคือตอนนี้สิ่งสำคัญจริง ๆ "บันทึก" คืออะไร? ฉันคิดว่าสำคัญกว่านั้นคือบันทึกชนิดดั้งเดิมดั้งเดิมที่แท้จริงคือเลเยอร์นามธรรมอีกชั้นหนึ่งดังนั้นจะไม่มีอัตโนมัติอาจมี:MyClass::RecordTy record = myObj.FindRecord (something)
paul23

2
@ paul23: การใช้อัตโนมัติกับประเภทนั้นทำให้คุณได้รับอะไรถ้าคุณคัดค้านเพียงอย่างเดียวคือ "ฉันไม่รู้ว่าจะใช้สิ่งนี้อย่างไร" อาจทำให้คุณดูดีอยู่ดี
GManNickG

3
@GManNickG มันบอกประเภทที่ไม่สำคัญของฉันอย่างแน่นอน
paul23

10

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

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

for( std::map<std::pair<Foo,Bar>, std::pair<Baz, Bot>, std::less<BazBot>>::const_iterator it = things_.begin(); it != things_.end(); ++it )

.. หรือ...

for( auto it = things_.begin(); it != things_.end(); ++it )

บางคนบอกว่าข้อที่สองเข้าใจง่ายกว่าคนอื่น ๆ อาจบอกเป็นครั้งแรก แต่คนอื่นอาจบอกว่าการใช้อย่างไม่มีเหตุผลautoอาจมีส่วนทำให้โปรแกรมเมอร์ที่ใช้ลดลง แต่นั่นเป็นอีกเรื่องหนึ่ง


4
+1 ฮ่าฮ่าทุกคนกำลังนำเสนอstd::mapตัวอย่างนอกจากนี้ยังมีข้อโต้แย้งแม่แบบที่ซับซ้อน
Nawaz

1
@Nawaz: มันเป็นเรื่องง่ายที่จะสร้างชื่อเทมเพลตที่ยาวมาก ๆ โดยใช้maps :)
John Dibling

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

1
@PlasmaHH ไม่สามารถวนซ้ำกับตัววนซ้ำทั้งหมดได้ด้วยการใช้ช่วงforเช่นหากตัววนซ้ำถูกทำให้ใช้ไม่ได้ในลูปบอดี้และจำเป็นต้องมีการเพิ่มขึ้นล่วงหน้าหรือไม่เพิ่มเลย
โจนาธาน Wakely

@PlasmaHH: ในกรณีของฉัน MSVC10 ไม่ได้ทำตามช่วงสำหรับลูป เนื่องจาก MSVC10 เป็นแบบทดสอบ C-11 แบบ go-to C ++ 11 ฉันจึงไม่ค่อยมีประสบการณ์กับพวกเขามากนัก
John Dibling

8

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

Bottom line: ใช้autoเมื่อมันช่วย: คือตัววนซ้ำในการวนซ้ำ อย่าใช้มันเมื่อมันทำให้ผู้อ่านต่อสู้เพื่อค้นหาประเภท


6

ฉันค่อนข้างประหลาดใจที่ไม่มีใครชี้ให้เห็นว่าอัตโนมัติช่วยได้หากไม่มีประเภทที่ชัดเจน ในกรณีนี้คุณสามารถแก้ไขปัญหานี้ได้โดยใช้ #define หรือ typedef ในเทมเพลตเพื่อค้นหาประเภทที่ใช้งานได้จริง

สมมติว่าคุณมีฟังก์ชั่นซึ่งจะส่งคืนบางสิ่งที่มีประเภทเฉพาะแพลตฟอร์ม:

#ifdef PLATFROM1
__int256 getStuff();
#else //PLATFORM2
__int128 getStuff();
#endif

คุณต้องการใช้แม่มดไหม?

#ifdef PLATFORM1
__int256 stuff = getStuff();
#else
__int128 stuff = getStuff();
#endif

หรือเพียงแค่

auto stuff = getStuff();

แน่นอนว่าคุณสามารถเขียน

#define StuffType (...)

เช่นกันที่ใดที่หนึ่ง แต่ทำ

StuffType stuff = getStuff();

จริงบอกอะไรเพิ่มเติมเกี่ยวกับประเภทของ x? มันบอกว่ามันคือสิ่งที่ถูกส่งคืนจากที่นั่น แต่มันคือสิ่งที่เป็นอัตโนมัติ นี่เป็นแค่การซ้ำซ้อน - 'สิ่งของ' เขียน 3 ครั้งที่นี่ - ในความคิดของฉันทำให้อ่านน้อยกว่ารุ่น 'อัตโนมัติ'


5
วิธีที่ถูกต้องในการจัดการเฉพาะประเภทแพลตฟอร์มคือการtypedefให้พวกเขา
cmaster

3

การอ่านเป็นเรื่องส่วนตัว คุณจะต้องดูสถานการณ์และตัดสินใจว่าอะไรดีที่สุด

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

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

จากนั้นก็มีความมั่นคง; คุณคิดว่าสำคัญแค่ไหน? คุณต้องการให้อัตโนมัติในบางส่วนและประกาศอย่างชัดเจนในส่วนอื่น ๆ หรือวิธีหนึ่งที่สอดคล้องกันตลอด?


2

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


2
ที่ดีที่สุดคุณจะได้รับคนเขียนชื่อตัวแปรที่ต้องการmyComplexDerivedTypeทำขึ้นสำหรับประเภทที่หายไปซึ่ง clutters รหัสโดยการทำซ้ำประเภท (ทุกที่มีการใช้ตัวแปร) และที่ดึงดูดให้ผู้คนละเว้นวัตถุประสงค์ของตัวแปรในชื่อของมัน . ประสบการณ์ของฉันคือไม่มีอะไรที่ไม่ก่อผลเช่นเดียวกับการวางอุปสรรคในรหัส
cmaster

2

ฉันมีสองแนวทาง:

  • หากประเภทของตัวแปรชัดเจนน่าเบื่อที่จะเขียนหรือยากที่จะตรวจสอบการใช้งานอัตโนมัติ

    auto range = 10.0f; // Obvious
    
    for (auto i = collection.cbegin(); i != cbegin(); ++i) // Tedious if collection type
    // is really long
    
    template <typename T> ... T t; auto result = t.get(); // Hard to determine as get()
    // might return various stuff
  • หากคุณต้องการการแปลงที่เฉพาะเจาะจงหรือประเภทผลลัพธ์ไม่ชัดเจนและอาจทำให้เกิดความสับสน

    class B : A {}; A* foo = new B(); // 'Convert'
    
    class Factory { public: int foo(); float bar(); }; int f = foo(); // Not obvious

0

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

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

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