คำหลักอัตโนมัติ C ++ 11 มีมากเกินไป


218

ฉันใช้autoคำหลักใหม่ที่มีอยู่ในมาตรฐาน C ++ 11 สำหรับประเภท templated ที่ซับซ้อนซึ่งเป็นสิ่งที่ฉันเชื่อว่าได้รับการออกแบบมา แต่ฉันก็ใช้มันเพื่อสิ่งต่าง ๆ เช่น:

auto foo = std::make_shared<Foo>();

และน่าสงสัยมากขึ้นสำหรับ:

auto foo = bla(); // where bla() return a shared_ptr<Foo>

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

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

หมายเหตุด้านข้าง: คำถามนี้ถูกย้ายไปที่ SE.Programmers แล้วกลับไปที่ Stack Overflow การสนทนาเกี่ยวกับเรื่องนี้สามารถพบได้ในคำถามเมตานี้


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

15
@heishe ฉันเพิ่มคำอธิบาย หากคุณอ่านคำถามโดยทั่วไปดูเหมือนว่าจะเป็นการถามความเห็นส่วนตัว แต่จริงๆแล้วถ้าคุณใช้autoคำหลักคุณจะรู้ว่าควรใช้อย่างไร นั่นคือสิ่งที่ฉันถามในฐานะคนที่เพิ่งรู้จักคุณลักษณะนี้ฉันควรจะใช้มันอย่างไร
อลันทัวริง

13
ฉันได้เห็นการสนทนานี้ทุกที่เมื่อ C # แนะนำvar(นั่นคือเมื่อคนได้รับมากกว่าความคิดที่ว่ามันไม่ได้พิมพ์แบบไดนามิกหลังจากทั้งหมด) หากคุณต้องการคุณสามารถเริ่มต้นด้วยคำถามนี้และทำตามคำถามที่เกี่ยวข้อง
R. Martinho Fernandes

2
@Lex: มีบางอย่างที่ถูกกฎหมายหรือไม่ การเรียกสิ่งที่ "ไม่ดี" ที่ถูกกฎหมายนั้นเป็นไปตามความหมาย นั่นคือการเรียกว่าauto foo = bla();"ไม่ดี" เป็นความเห็นที่ชัดเจนไม่ใช่ความจริงซึ่งทำให้คำถามนี้และตอบการสนทนาซึ่งทำให้มันเกี่ยวข้องกับโปรแกรมเมอร์ SE ซึ่งเป็นสิ่งที่การลงมติอย่างใกล้ชิดบ่งบอก / ยัก
ildjarn

3
มุมมอง Herb Sutter ในเรื่องนี้: herbutter.com/2013/06/13/…
rafak

คำตอบ:


131

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

my_multi_type::nth_index<2>::type::key_type::composite_key_type::
    key_extractor_tuple::tail_type::head_type::result_type

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

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

นี่คือตัวอย่างบางส่วน:

auto foo = std::make_shared<Foo>();   // obvious
auto foo = bla();                     // unclear. don't know which type `foo` has

const size_t max_size = 100;
for ( auto x = max_size; x > 0; --x ) // unclear. could lead to the errors
                                      // since max_size is unsigned

std::vector<some_class> v;
for ( auto it = v.begin(); it != v.end(); ++it )
                                      // ok, since I know that `it` has an iterator type
                                      // (don't really care which one in this context)

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

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

45
ไม่ว่า C ++ diehards จะชอบหรือไม่ก็ตาม C ++ 0x จะดึงดูดผู้ที่ไม่เคยใช้ C ++ และสิ่งเหล่านั้นจะใช้ระบบอัตโนมัติทั่วสถานที่
ศ. Falken

6
@ R.MartinhoFernandes - ไม่มีสิ่งเดียวที่เป็นที่ชัดเจนว่าสิ่งที่ ผลตอบแทนที่คุณจะให้มันไปbla() foo
Luis Machuca

8
@ LuisMachuca คำตอบของฉันเป็นวิธีที่ใช้แก้มในการบอกว่ามันไม่สุจริตที่จะให้ตัวอย่างหนังสือเรียนของตัวแปรที่ไม่ดีและการตั้งชื่อฟังก์ชั่นและการตำหนิการขาดการอ่านที่อนุมานประเภท
R. Martinho Fernandes

62

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


12
ฉันจะพูดโดยเฉพาะอย่างยิ่ง 'const auto &'
Viktor Sehr

2
ใช้งานได้ดีขึ้นauto&&ในสถานการณ์ที่ซับซ้อนedmundv.home.xs4all.nl/blog/2014/01/28/…
KindDragon

2
@KindDragon: นั่นเป็นกฎง่ายๆ แต่ฉันชอบที่จะใช้const auto&หรือconst autoถ้าฉันไม่ต้องการที่จะกลายพันธุ์หรือย้าย
Jon Purdy

" ใช้งานอัตโนมัติได้ทุกที่ " นี้ไม่ได้หมายความว่าผมควรจะเขียนauto str = std::string();แทนstd::string str;?
Calmarius

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

52

ง่าย. ใช้มันเมื่อคุณไม่สนใจประเภทนั้น ตัวอย่างเช่น

for (const auto & i : some_container) {
   ...

ทั้งหมดที่ฉันสนใจนี่iคือสิ่งที่อยู่ในภาชนะ

มันเป็นเหมือน typedefs

typedef float Height;
typedef double Weight;
//....
Height h;
Weight w;

นี่ผมไม่สนใจว่าhและwมีลอยหรือคู่เท่านั้นที่พวกเขามีสิ่งที่ประเภทเหมาะที่จะแสดงความสูงและน้ำหนัก

หรือพิจารณา

for (auto i = some_container .begin (); ...

นี่คือทั้งหมดที่ฉันสนใจคือมันเป็นตัววนซ้ำที่เหมาะสมสนับสนุนoperator++()มันเหมือนกับเป็ดที่พิมพ์ในแง่นี้

นอกจากนี้รูปแบบของลูกแกะก็ไม่สามารถสะกดได้เช่นกันดังนั้นauto f = []...สไตล์ที่ดี ทางเลือกคือการหล่อstd::functionแต่ที่มาพร้อมกับค่าใช้จ่าย

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

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

ตัวอย่าง (ยืมมาจากคำตอบของคนอื่น):

auto i = SomeClass();
for (auto x = make_unsigned (y); ...)

ที่นี่เราสนใจสิ่งที่เป็นประเภทดังนั้นเราควรเขียนSomeclass i;และfor(unsigned x = y;...


1
หนอ ไม่ใช่เรื่องง่าย มันรวบรวมและวิ่งและคุณก็ยิงเองถ้าสิ่งของนั้นเป็นวัตถุที่ไม่สำคัญ - การทำซ้ำของคุณเรียกตัวสร้างสำเนาและตัวทำลายในทุกขั้นตอนของการทำซ้ำ หากคุณกำลังจะใช้ auto ใน iterator ที่เป็นช่วงสุ่มสี่สุ่มห้าก็น่าจะเป็น "for (const auto & item: some_container)" แทนที่จะเป็น "for (auto item: some_container)"
Don Hatch

2
ไม่เสมอไป แต่คุณอาจต้องการข้อมูลอ้างอิง แล้วอะไรล่ะ นั่นคือไม่มีอะไรautoจะทำอย่างไรกับ
spraff

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

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

43

ไปเลย ใช้autoทุกที่ที่ทำให้การเขียนโค้ดง่ายขึ้น

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

แต่ถ้าฉันทำงานกับโค้ดที่มีมากกว่าสองสามบรรทัดเช่น

auto foo = bla();

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


38

ในC ++ และหลังปี 2012ในถามอะไรเราแผงมีการแลกเปลี่ยนที่ยอดเยี่ยมระหว่างอังเดร Alexandrescu สกอตต์เมเยอร์สและสมุนไพร Sutter autoพูดถึงเมื่อจะใช้งานและไม่ได้ใช้งาน ข้ามไปยังนาที 25:03 สำหรับการสนทนา 4 นาที ทั้งสามลำโพงให้จุดที่ดีที่ควรจะเก็บไว้ในใจเมื่อจะไม่ได้autoใช้

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

  1. มันเจ็บการอ่าน
  2. มีความกังวลเกี่ยวกับการแปลงชนิดอัตโนมัติ (เช่นจากตัวสร้างการกำหนดประเภทกลางของแม่แบบการแปลงโดยนัยระหว่างความกว้างของจำนวนเต็ม)

การใช้เสรีexplicitช่วยลดความกังวลสำหรับหลังซึ่งช่วยลดระยะเวลาที่อดีตเป็นปัญหา

กล่าวซ้ำในสิ่งที่ Herb พูดว่า "ถ้าคุณไม่ได้ทำ X, Y และ Z ให้ใช้autoเรียนรู้ว่า X, Y และ Z คืออะไรและออกไปข้างนอกแล้วใช้autoที่ไหนก็ได้"


4
อาจจะคุ้มค่ากับการเชื่อมโยงกับ "เกือบอัตโนมัติทุกครั้ง" - herbutter.com/2013/08/12/…
Rob Starling

37

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

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

auto ที่นี่ไม่เจ็บการอ่าน

อีกตัวอย่างคือประเภทกฎตัวแยกวิเคราะห์ซึ่งสามารถยาวและซับซ้อน เปรียบเทียบ:

   auto spaces = space & space & space;

กับ

r_and_t<r_and_t<r_char_t<char>&, r_char_t<char>&>, r_char_t<char>&> spaces = 
   space & space & space;

ในทางกลับกันเมื่อเป็นที่รู้จักและเรียบง่ายมันจะดีกว่าถ้ามันระบุไว้อย่างชัดเจน:

int i = foo();

ค่อนข้างมากกว่า

auto i = foo();

2
แน่นอนว่าการมีช่วงของการวนซ้ำในภาษาทำให้ตัวอย่างแรกของคุณน่าตื่นเต้นน้อยลง 8v)
Fred Larson

@ เฟรด: ประเภทยังคงเป็นเรื่องยุ่งยาก (ฉันกำลังคิดว่าคอนเทนเนอร์ที่เชื่อมโยง)
Matthieu M.

3
@ เฟร็ด: ทุกครั้งที่คุณไม่มีขอบเขตbegin()และend()หรือขนาดขั้นตอนของคุณเป็นอย่างอื่นนอกจากขนาดหนึ่งหรือคุณกำลังแก้ไขคอนเทนเนอร์ในขณะที่คุณวนรอบการอิงตามคำสั่งจะไม่ช่วยคุณ
Dennis Zickefoose

6
@geotavros: และr_and_t<r_and_t<r_char_t<char>&, r_char_t<char>&>, r_char_t<char>&>ทำอย่างไร
Dennis Zickefoose

7
@geotavros: หรือคุณสามารถดูว่าspaceเป็นประเภทใดและค้นหาสิ่งนั้น นั่นคือข้อมูลที่มีประโยชน์มากขึ้น แต่อย่างใด ... หลังจากทั้งหมดปัญหาไม่ได้เป็น "ตัวแปรใหม่นี้คืออะไร" แต่เป็น "สิ่งที่space & space & spaceหมายความว่าอย่างไร" ประเภทของการแสดงออกที่แท้จริงเป็นเพียงเสียงรบกวน
Dennis Zickefoose

19

auto อาจเป็นอันตรายอย่างยิ่งเมื่อใช้ร่วมกับเทมเพลตการแสดงออกที่ใช้บ่อยโดยห้องสมุดพีชคณิตเชิงเส้นเช่น Eigen หรือ OpenCV

auto A = Matrix(...);
auto B = Matrix(...);
auto C = A * B; // C is not a matrix. It is a matrix EXPRESSION.
cout << C; // The expression is evaluated and gives the expected result.
... // <code modifying A or B>
cout << C; // The expression is evaluated AGAIN and gives a DIFFERENT result.

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

auto C = Matrix(A * B); // The expression is now evaluated immediately.

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

1
ไม่ว่าการA*Bแสดงออกจะถูกคัดลอกในautoตัวแปรหรืออย่างอื่นพฤติกรรมที่คุณอธิบายยังคงมีอยู่
xtofl

ไม่มีข้อผิดพลาดจะเกิดจากautoโดยใช้
Jim Balter

@JAB: มันถูกออกแบบมาเพื่อรองรับความเรียบง่ายและการทำงานร่วมกันระหว่างการดำเนินการตัวอย่างเช่นdiag(A * B)ไม่ต้องเสียเวลาในการคำนวณองค์ประกอบนอกแนวทแยง
Ben Voigt

ฉันยังเห็นรหัสเช่น 'for (auto o: vecContainer)' โดยที่ 'o' เป็นวัตถุหนักที่ถูกคัดลอกทุกครั้ง คำแนะนำของฉันจะใช้สำหรับสิ่งที่มันตั้งใจเดิมคือแม่แบบ (พร้อมแนวทางการหักยาก) และ typedef ลำบากของ แม้ว่าคุณจะต้องระมัดระวังและแยกแยะระหว่างรถยนต์กับรถยนต์ &
gast128

10

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

auto i = MyClass();

แทน

MyClass i;

นี่เป็นกรณีหนึ่งที่ฉันจะบอกว่ามันเป็นการละเมิดautoคำหลัก

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

เกี่ยวกับตัวอย่างนี้จากคำตอบที่ยอมรับ:

for ( auto x = max_size; x > 0; --x )

ในรหัสของฉันฉันยังคงใช้autoในกรณีนี้และถ้าฉันต้องการxที่จะไม่ได้ลงนามแล้วฉันจะใช้ฟังก์ชั่นยูทิลิตี้ชื่อพูดmake_unsignedซึ่งแสดงความกังวลของฉันอย่างชัดเจน:

for ( auto x = make_unsigned(max_size); x > 0; --x )

ข้อจำกัดความรับผิดชอบ: ฉันแค่อธิบายการใช้งานของฉันฉันไม่สามารถให้คำแนะนำ!


1
@ChristianRau: ไม่เหน็บแนม auto i = MyClass()โปรดทราบว่าผมไม่แนะนำให้ใช้
rafak

3
การติดตามผล: ดูที่: herbutter.com/2013/06/13/… . as_unsignedแนะนำให้ใช้เช่นหรือที่auto w = widget{};นั่น
rafak

3

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

เพียงเพื่อแสดงให้เห็นถึงการพิจารณาด้านล่างโปรแกรม c ++:

int main() {
    int x;
    int y = 0;
    y += x;
}

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

main.cpp: ในฟังก์ชัน 'int main ()':

main.cpp: 4: 8: คำเตือน : 'x' ถูกใช้โดยไม่กำหนดค่าเริ่มต้นในฟังก์ชั่นนี้ [-Wuninitialized]

y + = x;

    ^

================================================== =============================== ทีนี้ถ้าเราเปลี่ยนโปรแกรมของเราซึ่งใช้ autoแล้วคอมไพล์เราจะได้ข้อมูลดังนี้:

int main() {
    auto x;
    auto y = 0;
    y += x;
}

main.cpp: ในฟังก์ชัน 'int main ()':

main.cpp: 2: 10: ข้อผิดพลาด : การประกาศ 'auto x' ไม่มี initializer

 auto x;

      ^

ด้วย auto มันเป็นไปไม่ได้ที่จะใช้ตัวแปร uninitialized นี่คือประโยชน์ที่สำคัญซึ่งเราอาจได้รับ (ฟรี) ถ้าเราเริ่มต้นการใช้รถยนต์

แนวคิดนี้และแนวคิด C ++ ที่ยอดเยี่ยมทันสมัยอื่น ๆ อธิบายโดยผู้เชี่ยวชาญ C ++ Herb ShutterในการพูดคุยCppCon14ของเขา:

กลับไปสู่พื้นฐาน! Essentials of Modern C ++ Style


2
ใช่. และเพื่อระบุประเภทที่คุณสามารถเริ่มต้นเป็น 0i, 0u, 0l, 0ul, 0.0f, 0.0, หรือแม้กระทั่ง int (), ไม่ได้ลงชื่อ (), double (), ฯลฯ ()
Robinson

6
อาร์กิวเมนต์นี้ไร้สาระ คุณกำลังจะบอกว่า "คุณไม่สามารถลืม initialiser เพราะคุณจะได้รับการรวบรวมข้อผิดพลาด" autoแต่ที่คุณจะต้องจำไว้ว่าให้ใช้
การแข่งขัน Lightness ใน Orbit

1
@LightnessRacesinOrbit: ฉันรู้ (เรียนรู้) แนวคิดนี้จากการพูดคุยของ Herb Sutter ฉันพบว่ามันเป็นคำแนะนำเชิงตรรกะ / การปฏิบัติจากสมุนไพรคุยจึงคิดว่าจะแบ่งปันกับชุมชน
Mantosh Kumar

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

2

อันตรายอย่างหนึ่งที่ฉันสังเกตเห็นคือการอ้างอิง เช่น

MyBigObject& ref_to_big_object= big_object;
auto another_ref = ref_to_big_object; // ?

ปัญหาคือ another_ref ไม่ใช่การอ้างอิงจริงในกรณีนี้คือ MyBigObject แทน MyBigObject & คุณจะต้องคัดลอกวัตถุขนาดใหญ่โดยที่ไม่รู้ตัว

หากคุณได้รับการอ้างอิงโดยตรงจากวิธีการที่คุณอาจไม่คิดว่ามันคืออะไร

auto another_ref = function_returning_ref_to_big_object();

คุณจะต้องมี "auto &" หรือ "const auto &"

MyBigObject& ref_to_big_object= big_object;
auto& another_ref = ref_to_big_object;
const auto& yet_another_ref = function_returning_ref_to_big_object();

1

ใช้autoตำแหน่งที่เหมาะสมสำหรับประเภทที่จะอนุมาน หากคุณมีสิ่งที่คุณรู้ว่าเป็นจำนวนเต็มหรือคุณรู้ว่ามันเป็นสตริงเพียงแค่ใช้ int / std :: string ฯลฯ ฉันจะไม่กังวลเกี่ยวกับ "มากเกินไป" คุณสมบัติภาษาเว้นแต่ว่ามันจะถึงจุดที่ไร้สาระ หรือรหัสทำให้งงงวย

นั่นคือความคิดเห็นของฉัน


4
"ที่มันสมเหตุสมผล ... " เป็นส่วนที่ยุ่งยาก โปรแกรมเมอร์สามารถเข้าใจในรายละเอียดของการเข้ารหัสได้อย่างมากพวกเขาไม่รู้สึกว่ามีความหมายอะไรกับโปรแกรมเมอร์คนอื่น ๆ
DarenW

ถึงแม้ว่าฉันจะคิดว่ามันค่อนข้างง่ายที่จะบอกเมื่อมันคลุมเครือ หากมีข้อสงสัยให้ใช้ความคิดเห็น!
LainIwakura

1
มันจะไม่เหมาะสมที่จะใช้ประเภท?
มิคาอิล

นักพัฒนาบางคนจะบอกว่าควรสรุปประเภทนี้เสมอ !
Arafangion

ใช้ความคิดเห็น ...... หรือเพียงแค่ใช้ประเภทและไม่จำเป็นต้องแสดงความคิดเห็น?
user997112

1

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


0

TL; DR: ดูกฎของหัวแม่มือที่ด้านล่าง

คำตอบที่ได้รับการยอมรับให้เห็นกฎต่อไปนี้ของหัวแม่มือ:

ใช้autoเมื่อใดก็ตามที่ยากที่จะบอกวิธีเขียนประเภทตั้งแต่แรกเห็น แต่ประเภทของด้านขวามือของนิพจน์นั้นชัดเจน

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

auto x = f();

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

auto nugget = mine_gold();

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

ดังนั้นคำตอบของฉันคือ: ใช้autoเมื่อใดก็ตามที่คอมไพเลอร์อนุญาตเว้นแต่:

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

และนอกจากนี้ยังมี:

  • ต้องการให้ชื่อที่มีความหมาย (ซึ่งไม่มีชื่อประเภทของหลักสูตร) ​​ก่อนที่จะแทนที่autoด้วยประเภทที่เป็นรูปธรรม

-3

หนึ่งในประสบการณ์ที่ขมขื่นของฉันautoคือการใช้มันด้วยการแสดงออกแลมบ์ดา :

auto i = []() { return 0; };
cout<<"i = "<<i<<endl; // output: 1 !!!

ที่จริงที่นี่ได้รับการแก้ไขเพื่อตัวชี้การทำงานของi int(*)()นี่เป็นเพียงเรียบง่ายcoutแต่เพียงจินตนาการสิ่งที่ชนิดของข้อผิดพลาดการรวบรวม / templateรันไทม์ที่ไม่ดีก็สามารถทำให้เกิดเมื่อใช้กับ

คุณควรหลีกเลี่ยง autoด้วยนิพจน์ดังกล่าวและใส่returnประเภทที่เหมาะสม(หรือควบคุมdecltype())

การใช้งานที่ถูกต้องสำหรับตัวอย่างด้านบนจะเป็น

auto i = []() { return 0; }(); // and now i contains the result of calling the lambda  

7
คุณทำงานได้แย่มากในการอธิบายสิ่งที่เกี่ยวข้องกับรถยนต์ คุณสร้างฟังก์ชั่นแล้วพิมพ์ออกมา ... ตกลงไหม?
Dennis Zickefoose

5
@iammilind: และสิ่งที่เกี่ยวข้องกับรถยนต์?
R. Martinho Fernandes

7
ฉันพบว่ามันไม่น่าเป็นไปได้สูงที่คนจะต้องการใส่()ในท้ายที่สุด แลมบ์ดาอยู่ที่นั่นเพื่อทำหน้าที่เป็นฟังก์ชั่นและนั่นคือจุดที่การแปลงตัวชี้ฟังก์ชั่นมาจาก หากคุณต้องการเรียกมันทันทีทำไมต้องใช้แลมบ์ดาเลย? auto i = 0;ทำงานได้ค่อนข้างดี
R. Martinho Fernandes

4
อย่างน้อยคุณสามารถอธิบายสถานการณ์ที่auto x = []() { /* .... whatever goes in here ... */ }()ดีกว่าauto x = /* .... whatever goes in here ... */;เช่นในสิ่งเดียวกันโดยไม่ต้องแลมบ์ดาได้หรือไม่? ฉันพบว่าค่อนข้างไม่มีเหตุผลด้วยเหตุผลเดียวกันauto x = 42 + y - 42นั้นไม่มีจุดหมาย
R. Martinho Fernandes

6
-1 นี่ไม่ใช่ความผิดของอัตโนมัติ ประเภทของแลมบ์ดาไม่สามารถสะกดเพื่อให้อัตโนมัติจำเป็นถ้าคุณลืมที่จะเรียกฟังก์ชั่นแล้วว่าระวังของคุณเอง! คุณสามารถวางตัวชี้ฟังก์ชั่นที่ไม่ได้เรียกว่าลงใน C printf ได้อย่างส่งเดช
spraff
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.