ฉันควรใช้คุณสมบัติ 'อัตโนมัติ' C ++ 11 ใหม่โดยเฉพาะในลูปหรือไม่


20

ข้อดี / ข้อเสียของการใช้autoคำหลักโดยเฉพาะในลูปคืออะไร

for(std::vector<T>::iterator it = x.begin(); it != x.end(); it++ )
{
   it->something();
}

for(std::map<T>::iterator it = x.begin(); it != x.end(); it++ )
{
   it->second->something();
}

for(auto it = x.begin(); it != x.end(); it++ )
{
   it->??
}

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

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


3
รอ! มีการต่อสู้ว่าจะใช้varหรือไม่? ฉันคิดถึงสิ่งนั้น
สาธารณรัฐประชาธิปไตยประชาชนลาว

21
ยิ่งไปกว่านั้นคุณสามารถใช้for (auto& it : x)(หรือไม่มีการอ้างอิงหากคุณต้องการคัดลอก)
Tamás Szelei

7
ดูเหมือนว่าฉันถ้าคุณกำลังเขียนห่วงย้ำกว่าเนื้อหาของการxและคุณไม่ได้รู้ว่าสิ่งxคือคุณไม่ควรเขียนห่วงว่าในสถานที่แรก ;-)
nikie

@fish: กฎสำหรับช่วงลูป แต่ฉันจะอวดความคิดและทำ: 'for (T & it: x)' แทนเมื่อใช้ range for-loops ตามที่ฉันรู้สึกว่าใช้รถยนต์มีข้อมูลน้อยกว่า ชนิดของการใช้ผิดวิธีอัตโนมัติในใจของฉัน
martiert

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

คำตอบ:


38

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

boost::multi_map<NodeType, indexed_by<ordered_unique<identity<NodeType>>, hashed_non_unique<identity<NodeType>, custom_hasher>>::iterator_type<0> it

นั่นไม่ใช่แม้แต่แบบเต็มรูปแบบ ฉันพลาดข้อโต้แย้งของแม่แบบคู่


8
+1 สำหรับตัวอย่าง แต่ยังบอกบางอย่างเกี่ยวกับสถานะของ "modern" C ++
zvrba

22
@zvrba: ใช่ - ว่าสิ่งอำนวยความสะดวกทั่วไปมีประสิทธิภาพมากกว่า C #
DeadMG

4
นั่นคือสิ่งที่ typedef มีไว้สำหรับ
gbjbaanb

19
@gbjbaanb ไม่นั่นเป็นสิ่งที่autoมีไว้สำหรับ โดยการออกแบบ. typedefช่วย แต่autoช่วยได้มากขึ้น
Konrad Rudolph

1
typedef ทำให้อาร์กิวเมนต์นั้น "ไม่ใช้อัตโนมัติทำให้เป็นแบบยาวจริงๆ"
Michael

28

ในตัวอย่างของคุณ:

for(auto it = x.begin(); it != x.end(); i++)
{
  it->??
}

จะต้องมีการประกาศให้xมองเห็นได้ ดังนั้นประเภทของitควรชัดเจน หากประเภทของxไม่ชัดเจนแสดงว่าวิธีนั้นยาวเกินไปหรือคลาสมีขนาดใหญ่เกินไป


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

@ Max: ใช้xเป็นตัวอย่างทั่วไปเท่านั้นฉันมักจะใช้ชื่อตัวแปรอธิบายค่อนข้าง
ผู้ใช้

@ ผู้ใช้แน่นอนฉันไม่ได้คิดว่าเป็นตัวอย่างในโลกแห่งความเป็นจริง;)
สูงสุด

14

คัดค้าน ! คำถามที่โหลด

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

for(std::vector<T>::iterator it = x.begin(); it != x.end(); i++)
{
   it->???
}

for(std::map<T>::iterator it = x.begin(); it != x.end(); i++)
{
   it->second->???
}

ที่นั่น ปัญหาเดียวกันแม้ว่าคุณจะไม่ได้ใช้autoปัญหาเดียวกันแม้ว่าคุณไม่ได้ใช้

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

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

เกี่ยวกับvar(ตั้งแต่คุณอย่างชัดเจนกล่าวถึงมัน) นอกจากนี้ยังมีความเห็นเป็นเอกฉันท์ใหญ่ในชุมชน C # สำหรับvarใช้ ขัดแย้งกับการใช้งานโดยทั่วไปมักจะสร้างขึ้นบนความล้มเหลว


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

1
@Ptolemy และจุดของฉันคือในอีกสองรหัสที่คุณยังไม่ทราบ (โดยทั่วไป) จะใส่อะไรต่อไปจะเป็นสีขาวขุ่นให้กับผู้ใช้เป็นT autoแต่ก็ควรจะดีและอื่น ๆ ไม่ได้! ไม่สมเหตุสมผลเลย ในกรณีของ OP Tเป็น stand-in สำหรับประเภทที่กำหนดเอง ในรหัสจริงมันอาจเป็นการใช้เทมเพลต(for typename std::vector<T>::iterator…)หรือคลาสอินเตอร์เฟส ในทั้งสองกรณีประเภทจริงจะถูกซ่อนจากผู้ใช้ แต่เราก็ยังเขียนโค้ดดังกล่าวเป็นประจำโดยไม่มีปัญหา
Konrad Rudolph

1
จริงๆแล้วใช่คุณทำ ถ้าเป็นเวกเตอร์คุณรู้ว่าคุณต้องทำ -> แล้วคุณจะสามารถเข้าถึงชนิดข้อมูลของคุณได้ หากเป็นแผนที่คุณรู้ว่าคุณต้องทำ -> second-> จากนั้นคุณสามารถเข้าถึงประเภทข้อมูลของคุณได้หากอัตโนมัติคุณไม่ทราบว่าคุณต้องทำอะไรเพื่อเข้าถึงประเภทข้อมูลของคุณ ดูเหมือนว่าคุณจะสับสน "สิ่งที่เป็นชนิดข้อมูลที่มีอยู่ในคอลเลกชัน STL" กับ "เรามีประเภทคอลเลกชัน STL" อัตโนมัติทำให้ปัญหานี้แย่ลง
Michael Shaw

1
@Ptolemy autoทุกข้อโต้แย้งเหล่านี้เป็นเพียงความจริงเมื่อใช้ ไม่สำคัญที่จะเห็นว่าการดำเนินงานxรองรับอะไรจากบริบท ในความเป็นจริงประเภทนี้จะให้ข้อมูลเพิ่มเติมแก่คุณไม่ว่าในกรณีใดกรณีหนึ่งคุณต้องมีรอง (IDE, เอกสาร, ความรู้ / หน่วยความจำ) เพื่อบอกชุดการทำงานที่รองรับ
Konrad Rudolph

1
@Ptolemy นั่นคือเพียงความจริงถ้าคุณอยู่ในสถานการณ์ที่ซับซ้อนอย่างที่คุณไม่ทราบว่าbeginผลตอบแทน แต่คุณจะรู้ว่าสิ่งที่std::vector<>::iteratorเป็น และคุณต้องใช้เครื่องมือการเขียนโปรแกรมที่ไม่ดีซึ่งไม่สามารถให้ข้อมูลนี้กับคุณได้เล็กน้อย สิ่งนี้มีความซับซ้อนสูง ในความเป็นจริงคุณรู้ทั้งbeginและiteratorหรือไม่และคุณควรใช้ IDE หรือโปรแกรมแก้ไขที่สามารถทำให้ข้อมูลที่เกี่ยวข้องพร้อมใช้งานได้ IDE และการเขียนโปรแกรมที่ทันสมัยทุกตัวสามารถทำสิ่งนั้นได้
Konrad Rudolph

11

มือโปร

รหัสของคุณ:

for(std::vector<T>::iterator it = x.begin(); it != x.end(); i++)

จะไม่คอมไพล์เนื่องจากชื่อที่ขึ้นกับเทมเพลต

นี่คือไวยากรณ์ที่ถูกต้อง:

for( typename std::vector<T>::iterator it = x.begin(); it != x.end(); i++)

ตอนนี้ดูระยะเวลาที่การประกาศประเภทคืออะไร สิ่งนี้จะบอกว่าทำไมautoคำหลักถูกนำมาใช้ นี้ :

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

กระชับยิ่งขึ้น ดังนั้นนี่คือมืออาชีพ


CON

คุณต้องระวังหน่อย ด้วยคำหลักautoคุณจะได้รับประเภทที่คุณประกาศ

ตัวอย่างเช่น :

std::vector< int > v{ 1, 2, 3, 4 };
for ( auto it : v )
{
  ++ it;   // ops modifying copies of vector's elements
}

VS

std::vector< int > v{ 1, 2, 3, 4 };
for ( auto & it : v )   // mind the reference
{
  ++ it;   // ok, vector's elements modified
}

เพื่อสรุป: ใช่คุณควร แต่ไม่ควรใช้มากเกินไป บางคนมักจะใช้มันมากเกินไปและวางอัตโนมัติทุกที่อย่างเช่นในตัวอย่างถัดไป:

auto i = 0;

VS

int i = 0;

auto i = 0. รู้สึกผิด ผมทำนั่น. แต่นั่นเป็นเพราะฉันรู้ว่าเป็นความหมายของประเภท0 int(และค่าคงที่ฐานแปด ;-))
Laurent LA RIZZA

6

ใช่คุณควรจะ! autoไม่ลบประเภท; แม้ว่าคุณ "ไม่รู้" x.begin()คืออะไรคอมไพเลอร์รู้และจะรายงานข้อผิดพลาดหากคุณพยายามใช้ประเภทผิด นอกจากนี้ยังไม่ผิดปกติที่จะเลียนแบบmapด้วยvector<pair<Key,Value>>ดังนั้นรหัสที่ใช้autoจะทำงานได้ดีกับทั้งพจนานุกรม


4

ใช่คุณควรใช้autoเป็นกฎเริ่มต้น มันมีข้อได้เปรียบแบบดิบเหนือการระบุประเภทอย่างชัดเจน:

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

ที่นี่คุณมีทางเลือก นอกจากนี้ยังมีกรณีที่คุณไม่มีทางเลือก:

  • อนุญาตให้มีการประกาศตัวแปรชนิดที่ไม่สามารถตัดทอนได้เช่นเดียวกับแลมบ์ดา

หากว่าคุณรู้แน่ชัดว่าautoมันทำอะไรก็ไม่มีข้อเสีย

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