มีข้อเสียในการประกาศตัวแปรที่มี auto ใน C ++ หรือไม่


143

ดูเหมือนว่าautoเป็นคุณสมบัติที่มีความสำคัญพอสมควรที่จะเพิ่มใน C ++ 11 ที่ดูเหมือนว่าจะเป็นไปตามภาษาที่ใหม่กว่ามากมาย เช่นเดียวกับภาษาอย่าง Python ฉันไม่เคยเห็นการประกาศตัวแปรที่ชัดเจน (ฉันไม่แน่ใจว่ามันเป็นไปได้โดยใช้มาตรฐาน Python หรือไม่)

มีข้อเสียเปรียบในการใช้autoประกาศตัวแปรแทนที่จะประกาศอย่างชัดเจนหรือไม่?


1
ดูเพิ่มเติม: stackoverflow.com/questions/6434971/… , stackoverflow.com/questions/15254461/… , stackoverflow.com/questions/6900459/… , stackoverflow.com/questions/8430053/is-c11-auto-type-dangerousและบางทีคนอื่น ๆ ไม่แน่ใจว่าสิ่งเหล่านี้ซ้ำซ้อนจริงหรือไม่ แต่เป็นของผู้สมัครแน่นอน
Cody Gray

3
ข้อเสียเพียงอย่างเดียวที่ฉันพบคือเมื่อฉันต้องย้ายพอร์ตโค้ดไปยังแพลตฟอร์ม (คอนโซล) ซึ่งคอมไพเลอร์ไม่สนับสนุน (และไม่มีเจตนาสนับสนุน) คุณสมบัติ C ++ 11!
Sam

7
เพื่อความสมบูรณ์ GotW # 94 "อัตโนมัติเกือบตลอดเวลา": herbsutter.com/2013/08/12/…
Richard Critten

1
ฉันกำลังฟัง cppcast และมีการกล่าวถึง clash b / w auto และรายการ initializers ฉันจะลองหาพอดคาสต์นั้น
Abhinav Gauniyal

2
ข้อเสียแรกที่ฉันคิดว่าส่งผลกระทบต่อความสามารถในการอ่านของรหัส
ggrr

คำตอบ:


111

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

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

รับการประกาศเช่น

auto result = CallSomeFunction(x,y,z);

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

auto result = CallSomeFunction(a,y,z);

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

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

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


58
เป็นที่น่าสนใจที่จะทราบว่าหากตัวอย่างดังกล่าวเป็นข้อเสียเปรียบในการใช้autoภาษาที่พิมพ์เป็ดส่วนใหญ่จะได้รับผลกระทบจากการออกแบบ!
Leben Asa

11
หากCallSomeFunction()ผลตอบแทนที่แตกต่างกันประเภทขึ้นอยู่กับลำดับของการขัดแย้งของตนว่าเป็นข้อบกพร่องของการออกแบบCallSomeFunction(), autoไม่ได้เป็นปัญหาของ หากคุณไม่ได้อ่านเอกสารของฟังก์ชั่นที่คุณใช้ก่อนที่จะใช้มันว่าเป็นข้อบกพร่องของโปรแกรมเมอร์, autoไม่ได้เป็นปัญหาของ - แต่ฉันเข้าใจว่าคุณเล่นเป็นผู้สนับสนุนของปีศาจที่นี่มันเป็นเพียงแค่ว่า Nir Friedman มีคดีที่ดีกว่ามาก
DevSolar

16
@DevSolar: ทำไมT CallSomeFunction(T, int, int)ข้อบกพร่องของการออกแบบ? เห็นได้ชัดว่ามัน "ส่งกลับประเภทที่แตกต่างกันขึ้นอยู่กับลำดับของการขัดแย้ง"
MSalters

9
"ข้อเสียเปรียบหลักก็คือโดยการใช้autoคุณไม่จำเป็นต้องรู้ประเภทของวัตถุที่ถูกสร้างขึ้น" คุณสามารถอธิบายอย่างละเอียดว่าเหตุใดจึงเป็นปัญหาautoและไม่ใช่ปัญหาของ Tem Temion ทำไมถึงauto result = foo();ไม่ดี แต่foo().bar()ไม่ใช่?
Angew ไม่ภูมิใจใน SO

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

76

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

auto x = my_obj.method_that_returns_reference();

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

const auto& stuff = *func_that_returns_unique_ptr();

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

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


ทำไมคุณสามารถคัดลอกวัตถุราคาแพงเริ่มต้นด้วย
Laurent LA RIZZA

3
@ LaurentLARIZZA: บางคลาสมีตัวสร้างสำเนาเพียงเพราะบางครั้งก็จำเป็น (เช่นอินสแตนซ์ของstd::vector) การทำสำเนามีราคาแพงไม่ใช่ทรัพย์สินของคลาส แต่เป็นของแต่ละวัตถุ ดังนั้นmethod_that_returns_referenceอาจหมายถึงวัตถุของคลาสที่มีตัวสร้างการคัดลอก แต่วัตถุที่เกิดขึ้นจะค่อนข้างแพงคัดลอก (และไม่สามารถย้ายจาก)
Marc van Leeuwen

@MarcvanLeeuwen: ถ้าวัตถุที่มีราคาแพงในการคัดลอกและไม่สามารถเคลื่อนย้ายจากทำไมมันจะถูกเก็บไว้ในstd::vector? (เพราะอาจใช่หรือเพราะคุณไม่ได้ควบคุมชั้นเรียน แต่นั่นไม่ใช่ประเด็น) หากการคัดลอกมีราคาแพง (และไม่มีทรัพยากรเพราะคัดลอกได้) ทำไมไม่ใช้ COW บนวัตถุ ตำแหน่งข้อมูลถูกฆ่าโดยขนาดของวัตถุ
Laurent LA RIZZA

2
@LaurentLARIZZA ไม่ใช่ตัวอย่างของบางสิ่งที่เก็บไว้ในเวกเตอร์ที่มีราคาแพงเพียงเวกเตอร์ปกติ <double> มีราคาแพงในการคัดลอกมันเป็นการจัดสรรฮีป + O (N) การเคลื่อนไหวเป็นปลาเฮอริ่งแดง บรรทัดแรกที่ฉันแสดงจะคัดลอกไม่ใช่ย้ายยกเว้นการอ้างอิงที่ส่งคืนคือการอ้างอิง rvalue วัวไม่ได้อยู่ที่นี่หรือที่นั่น ความจริงก็คือการคัดลอกวัตถุมีราคาแพงมักจะมีอยู่เสมอ
Nir Friedman

4
@Yakk ไม่สามารถทำเช่นนั้นได้อย่างปลอดภัยเพราะมันสามารถเชือด สิ่งเดียวที่ปลอดภัยที่สามารถทำได้คือการ= deleteโอเวอร์โหลด แม้ว่าโดยทั่วไปสิ่งที่คุณพูดเป็นวิธีแก้ปัญหา นี่คือหัวข้อที่ฉันสำรวจถ้าคุณสนใจ: nirfriedman.com/2016/01/18/… .
Nir Friedman

51

คำตอบอื่น ๆ ที่กล่าวถึงข้อเสียเปรียบเช่น "คุณไม่รู้จริง ๆ ว่าประเภทของตัวแปรคืออะไร" ฉันจะบอกว่านี่เป็นส่วนใหญ่ที่เกี่ยวข้องกับการตั้งชื่อระเบียบเลอะเทอะในรหัส หากอินเทอร์เฟซของคุณมีชื่อชัดเจนคุณไม่จำเป็นต้องสนใจประเภทที่แน่นอน แน่นอนauto result = callSomeFunction(a, b);ว่าไม่ได้บอกอะไรคุณมากนัก แต่auto valid = isValid(xmlFile, schema);บอกให้คุณใช้มากพอvalidโดยไม่ต้องสนใจว่ามันจะเป็นแบบไหน if (callSomeFunction(a, b))ท้ายที่สุดด้วยเพียงแค่คุณจะไม่ทราบประเภททั้ง เช่นเดียวกันกับวัตถุชั่วคราว subexpression อื่น ๆ autoดังนั้นผมจึงไม่คิดว่านี่เป็นอุปสรรคที่แท้จริงของ

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

SomeType operator* (const Matrix &lhs, const Vector &rhs);

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

MultExpression<Matrix, Vector> operator* (const Matrix &lhs, const Vector &rhs);

ตอนนี้ปัญหาคือว่าMultExpression<Matrix, Vector>จะเก็บ a const Matrix&และconst Vector&ภายใน; มันคาดว่าจะแปลงเป็นVectorก่อนสิ้นนิพจน์แบบเต็ม หากเรามีรหัสนี้ทุกอย่างดี:

extern Matrix a, b, c;
extern Vector v;

void compute()
{
  Vector res = a * (b * (c * v));
  // do something with res
}

อย่างไรก็ตามหากเราใช้autoที่นี่เราอาจมีปัญหา:

void compute()
{
  auto res = a * (b * (c * v));
  // Oops! Now `res` is referring to temporaries (such as (c * v)) which no longer exist
}

3
@NirFriedman คุณพูดถูกมันแข็งแกร่ง แต่จริงๆแล้วฉันรู้สึกว่าautoมีข้อเสียน้อยมากดังนั้นฉันจึงยืนหยัดด้วยความแข็งแกร่งนั้น และตัวอย่างอื่น ๆ ของพร็อกซี่ ฯลฯ รวมถึง "ผู้สร้างสตริง" และวัตถุที่คล้ายกันที่พบใน DSL
Angew ไม่ภูมิใจใน SO

2
ฉันถูกกัดโดยเทมเพลตการแสดงออกและautoก่อนหน้านี้โดยเฉพาะกับห้องสมุด Eigen มันเป็นเรื่องยากโดยเฉพาะอย่างยิ่งเพราะปัญหามักจะไม่ปรากฏในการสร้างข้อบกพร่อง
ด่าน

1
การใช้งานautoสามารถกัดเมื่อใช้ห้องสมุดเมทริกซ์อาร์มาดิลโลซึ่งใช้เทมเพลตการเขียนโปรแกรมเทมเพลตสำหรับการเพิ่มประสิทธิภาพ โชคดีที่นักพัฒนาได้เพิ่มฟังก์ชั่น. eval ()ซึ่งสามารถใช้เพื่อหลีกเลี่ยงปัญหากับauto
mtall

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

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

13

หนึ่งในข้อเสียก็คือว่าบางครั้งคุณไม่สามารถประกาศด้วยconst_iterator autoคุณจะได้รับตัววนซ้ำปกติ (ไม่ใช่ const) ในตัวอย่างของรหัสที่นำมาจากคำถามนี้ :

map<string,int> usa;
//...init usa
auto city_it = usa.find("New York");

3
ดีที่คุณจะได้รับiteratorในกรณีใด ๆ constตั้งแต่แผนที่ของคุณคือไม่ใช่ ถ้าคุณต้องการที่จะแปลงเป็นconst_iteratorทั้งระบุชนิดตัวแปรอย่างชัดเจนตามปกติหรือแยกวิธีการเพื่อให้แผนที่ของคุณเป็น const findในบริบทของคุณ (ฉันชอบแบบหลัง SRP.)
Laurent LA RIZZA

auto city_it = static_cast<const auto&>(map).find("New York")? หรือด้วย C ++ auto city_if = std::as_const(map).find("New York")17
Dev Null

11

มันทำให้โค้ดของคุณยากขึ้นหรือน่าเบื่อเล็กน้อยในการอ่าน ลองนึกภาพบางอย่างเช่น:

auto output = doSomethingWithData(variables);

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


40
ไม่เสมอ. auto it = vec.begin();อ่านง่ายกว่าstd::vector<std::wstring>::iterator it = vec.begin();มาก ๆ
Jonathan Potter

4
ตกลง มันขึ้นอยู่กับกรณีการใช้งาน ฉันน่าจะแม่นยำมากกว่านี้
Skam

1
@SeeDart ใช่คนที่ใช้รถยนต์แบบที่ทำผิด
lciamp

6
"ติดตามลายเซ็นของฟังก์ชัน" หากไม่ใช่เมาส์วางหรือกดปุ่ม ("ตามสัญลักษณ์" / "ไปที่การประกาศ" / สิ่งที่เรียกว่า) คุณต้องกำหนดค่าตัวแก้ไขเพิ่มเติมหรือ เปลี่ยนเป็น IDE ซึ่งสามารถทำได้โดยไม่ต้องกำหนดค่า ... จุดของคุณยังคงใช้ได้
hyde

6
ฉันสังเกตเห็นว่าไม่ได้อยู่ใน IDE แต่ในช่องมองตาแมวเล็ก ๆ เมื่อตรวจสอบเช็ค! ด้วยระบบอัตโนมัติพวกมันก็ยากที่จะอ่านด้วยเหตุผลนั้น
JDługosz

10

เช่นนี้autoนักพัฒนาฉันเกลียด autoหรือมากกว่าฉันเกลียดวิธีที่ผู้คนในทางที่ผิด

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

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

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

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

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


3
which was great because typedef allowed the designer of the library to help you figure out what the return type should be, so that their library works as expected. When you use auto, you take away that control from the class's designer and instead ask the compiler to figure out what the type should beไม่ใช่เหตุผลที่ดีจริงๆ IMO up-to-date IDE ของ Visual Studio 2015 autoตัวอย่างเช่นช่วยให้คุณสามารถตรวจสอบประเภทของตัวแปรโดยโฉบเหนือ นี่คือ* * * * * * * * ตรงเช่นเดียวกับtypedefหนึ่ง
ไก่ปีกกว้าง

@JameyD: คุณพลาดจุดสำคัญหลายจุด: (1) อาร์กิวเมนต์ IDE ของคุณจะใช้งานได้เฉพาะในกรณีที่ประเภทเป็นรูปธรรมไม่ใช่เทมเพลต IDEs typename std::iterator_traits<It>::value_typeไม่อาจบอกคุณชนิดที่ถูกต้องในกรณีของประเภทขึ้นอยู่เช่น (2) จุดทั้งหมดคือประเภทที่อนุมานไม่จำเป็นต้อง"เหมือนกันทุกประการ" เป็นประเภทที่ถูกต้องซึ่งออกแบบโดยนักออกแบบก่อนหน้าของรหัส โดยการใช้autoคุณจะกำจัดความสามารถของนักออกแบบเพื่อระบุประเภทที่ถูกต้อง
user541686

โดยพื้นฐานแล้วคุณกำลังพูดถึงพร็อกซีซึ่งหนึ่งในคำตอบที่กล่าวถึงแล้ว เทมเพลตการแสดงออกและเวกเตอร์ <bool> ไร้สาระไม่ใช่รหัสประจำวันสำหรับคนส่วนใหญ่ ในสถานการณ์ส่วนใหญ่คุณไม่ต้องการการแปลงโดยนัยและมีการช่วยเหลืออัตโนมัติ Herb Sutter พูดถึงข้อดีของการใช้งานรถยนต์อย่างกว้างขวางในโพสต์บล็อกของเขาและไม่ได้เกี่ยวกับการกดแป้นและส่วนใหญ่ไม่เพียง แต่สำหรับรหัสทั่วไป นอกจากนี้ลิงค์แรกที่คุณให้โพสต์บล็อกเป็นเพียงคำแนะนำที่น่ากลัวเพียงอย่างเดียว (ซึ่งเป็นเหตุผลที่เขาถูกวิพากษ์วิจารณ์อย่างว่องไวในส่วนความเห็นของเขา)
Nir Friedman

@NirFriedman: "... vector<bool>ไร้สาระ" ... ให้อภัย? คุณคิดว่าbitsetมีการนำไปใช้อย่างไร หรือคุณคิดว่า bit container เป็นเรื่องไร้สาระทั้งหมด!
user541686

1
@NirFriedman: ไม่มีอะไรเกี่ยวกับเวกเตอร์ <bool> เป็นข่าวให้ฉัน สิ่งที่ฉันพยายามจะบอกคุณและคุณปฏิเสธอย่างโจ๋งครึ่มที่จะเข้าใจว่าสำหรับวัตถุประสงค์ของบิตเซ็ตคำถามนี้ไม่แตกต่างจากเวกเตอร์ <bool> - ทั้งคู่ใช้พร็อกซีเพราะพร็อกซี่ถือว่ามีประโยชน์ และความจริงที่ว่าพร็อกซี่มีประโยชน์คือความจริงที่คุณต้องยอมรับแทนที่จะใช้ชีวิตในการปฏิเสธ คุณช่วยหยุดเปลี่ยนสิ่งนี้เป็นการอภิปรายว่าคุณคิดว่าผู้รับมอบฉันทะมีประโยชน์หรือไม่? นั่นไม่ใช่ประเด็นถกเถียงและความเห็นของคุณที่มีต่อพวกเขาเป็นเพียงความคิดเห็นของคุณไม่ใช่ความจริงบางประการ
user541686

6

autoไม่มีข้อเสียต่อ seและฉันแนะนำให้ (hand- wavily) ใช้มันในทุกที่ในรหัสใหม่ จะช่วยให้รหัสของคุณตรวจสอบประเภทอย่างสม่ำเสมอและหลีกเลี่ยงการแบ่งส่วนที่ไม่มีเสียง (หากBเกิดขึ้นจากAและฟังก์ชั่นกลับมาAก็กลับBมาแล้วก็autoทำงานตามที่คาดไว้ในการเก็บค่าตอบแทนของมัน)

แม้ว่ารหัสดั้งเดิมรุ่น pre-C ++ 11 อาจขึ้นอยู่กับการแปลงโดยนัยที่เกิดจากการใช้ตัวแปรที่พิมพ์อย่างชัดเจน การเปลี่ยนตัวแปรที่พิมพ์อย่างชัดเจนเป็นautoอาจเปลี่ยนพฤติกรรมของรหัสดังนั้นคุณควรระมัดระวัง


Downvoting นั้นยุติธรรม แต่คุณกรุณาให้ความเห็นเกี่ยวกับสาเหตุได้ไหม
Laurent LA RIZZA

ฉันไม่ได้ลงคะแนนคุณ แต่autoมีข้อเสียต่อ se (หรืออย่างน้อย - หลายคนคิดว่ามัน) ลองพิจารณาตัวอย่างที่ให้ไว้ในคำถามที่สองในการอภิปรายอภิปรายนี้กับ Sutter, Alexandrescu และ Meyers: หากคุณมีauto x = foo(); if (x) { bar(); } else { baz(); }และfoo()ส่งคืนbool- จะเกิดอะไรขึ้นหากfoo()การเปลี่ยนแปลงเพื่อส่งคืน enum (สามตัวเลือกแทนสอง) autoรหัสจะยังคงทำงาน แต่ก่อให้เกิดผลที่ไม่คาดคิด
einpoklum

@einpoklum: และใช้boolแทนการautoเปลี่ยนแปลงอะไรในกรณีของ enum ที่ไม่ได้กำหนดขอบเขตหรือไม่? ผมอาจจะผิด (ไม่สามารถตรวจสอบได้ที่นี่) แต่ผมคิดว่าแตกต่างเพียงอย่างเดียวคือการแปลงที่เกิดขึ้นในการประกาศตัวแปรแทนในการประเมินผลของภาวะที่bool ifหากenumมีการกำหนดขอบเขตการแปลงเป็นboolจะไม่เกิดขึ้นหากไม่มีการแจ้งให้ทราบอย่างชัดเจน
Laurent LA RIZZA

4

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

# Python
a
a = 10       # OK
a = "10"     # OK
a = ClassA() # OK

// C++
auto a;      // Unable to deduce variable a
auto a = 10; // OK
a = "10";    // Value of const char* can't be assigned to int
a = ClassA{} // Value of ClassA can't be assigned to int
a = 10.0;    // OK, implicit casting warning

เนื่องจากautoมีการอนุมานระหว่างการรวบรวมจึงไม่มีข้อเสียเปรียบในขณะใช้งาน


1
ใช่มันเป็นสิ่งที่ทำtype()ในหลาม มันอนุมานประเภทมันไม่ได้สร้างตัวแปรใหม่ของประเภทนั้น
lciamp

2
@lciamp decltypeจริงที่จะเป็น autoสำหรับการมอบหมายตัวแปรโดยเฉพาะ
Cubic

4

สิ่งที่ไม่มีใครพูดถึงที่นี่จนถึงตอนนี้ แต่สำหรับตัวเองนั้นมีค่าคำตอบถ้าคุณถามฉัน

เนื่องจาก (แม้ว่าทุกคนควรทราบว่าC != C++) โค้ดที่เขียนใน C สามารถออกแบบมาเพื่อให้เป็นพื้นฐานสำหรับโค้ด C ++ และดังนั้นจึงได้รับการออกแบบโดยไม่ต้องใช้ความพยายามมากเกินไปที่จะเข้ากันได้กับ C ++ นี่อาจเป็นข้อกำหนดสำหรับการออกแบบ

ฉันรู้เกี่ยวกับกฎบางอย่างที่บางตัวสร้างที่กำหนดไว้ดีCไม่ถูกต้องC++และในทางกลับกัน แต่สิ่งนี้จะส่งผลให้เกิด executables ที่ใช้งานไม่ได้และ UB-clause ที่เป็นที่รู้จักซึ่งส่วนใหญ่แล้วจะสังเกตได้จากการวนลูปแปลก ๆ ทำให้เกิดการขัดข้องหรืออะไรก็ตาม (หรือแม้กระทั่งอาจไม่ถูกตรวจจับ

แต่autoนี่เป็นครั้งแรกที่1การเปลี่ยนแปลงนี้!

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

นั่นคือสิ่งที่เราควรจำไว้


1 อย่างน้อยครั้งแรกที่ฉันรู้


1
คุณจะได้รับข้อผิดพลาดของคอมไพเลอร์ต่อไปเมื่อพยายามคอมไพล์
ไก่ปีกกว้าง

@JameyD: จะทำอย่างไร? เหตุใดจึงต้องใช้ 2 สถานการณ์รหัสที่ถูกต้องซึ่งมีความหมายแตกต่างกัน resutl เคยผิดพลาด?
dhein

8
หากคุณพึ่งพา "no type implies int" ใน C คุณสมควรได้รับสิ่งที่ไม่ดีทั้งหมดที่คุณจะได้รับจากสิ่งนี้ และหากคุณไม่ได้ใช้มันการใช้autoตัวระบุคลาสหน่วยเก็บข้อมูลพร้อมกับประเภทจะทำให้คุณมีข้อผิดพลาดในการคอมไพล์ใน C ++ (ซึ่งเป็นสิ่งที่ดีในกรณีนี้)
Angew ไม่ภูมิใจใน SO

1
@ ดีมากนั่นคือกรณีที่ฉันพูดถึงใช่ ฉันไม่ได้ทำสิ่งนี้ แต่มันเป็นสิ่งที่ควรจำไว้เป็นอย่างน้อย
dhein

3

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


1
มี static_cast และ IIRC เช่น C + + Modern Modern Meyers ที่มีประสิทธิภาพแนะนำให้ใช้มันเพื่อระบุประเภทสำหรับตัวแปรที่พิมพ์อัตโนมัติ
hyde

2

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


2
นั่นไม่ใช่เรื่องขี้ขลาด นั่นคือสิ่งที่autoไม่เคยอนุมานการอ้างอิงหรือconstประเภท สำหรับการอ้างอิงคุณควรที่จะใช้ดีกว่าauto auto&&(การอ้างอิงสากล) หากชนิดไม่ถูกคัดลอกหรือเป็นเจ้าของทรัพยากรประเภทนั้นไม่ควรคัดลอกเพื่อเริ่มต้นด้วย
Laurent LA RIZZA

1

อีกตัวอย่างที่น่ารำคาญ:

for (auto i = 0; i < s.size(); ++i)

สร้างคำเตือน ( comparison between signed and unsigned integer expressions [-Wsign-compare]) เพราะiเป็น int ที่ลงนามแล้ว เพื่อหลีกเลี่ยงปัญหานี้คุณต้องเขียนเช่น

for (auto i = 0U; i < s.size(); ++i)

หรืออาจจะดีกว่า:

for (auto i = 0ULL; i < s.size(); ++i)

1
ใช่ฉันพบว่ามันน่ารำคาญเช่นกัน แต่หลุมในภาษาอยู่ที่อื่น สำหรับรหัสนี้จะพกพาอย่างแท้จริงสมมติว่าsizeผลตอบแทนที่size_tคุณจะต้องสามารถที่จะมีตัวอักษรเช่นsize_t 0zแต่คุณสามารถประกาศ UDL เพื่อทำสิ่งนี้ ( size_t operator""_z(...))
Laurent LA RIZZA

1
การคัดค้านทางทฤษฎีล้วนๆ: unsignedมีแนวโน้มว่าจะไม่ใหญ่พอที่จะเก็บค่าทั้งหมดของstd::size_tสถาปัตยกรรมหลักดังนั้นในกรณีที่ไม่น่าเป็นไปได้ที่ใครบางคนมีภาชนะบรรจุที่มีจำนวนมหาศาลขององค์ประกอบที่ไร้เหตุผลการใช้unsignedอาจทำให้เกิดวงวนไม่สิ้นสุด ของดัชนี ขณะนี้ไม่น่าเป็นปัญหาstd::size_tควรใช้เพื่อรับรหัสสะอาดที่สัญญาณความตั้งใจ ฉันไม่แน่ใจว่าแม้unsigned long longจะมีการรับรองอย่างเพียงพอที่จะพอเพียง แต่ในทางปฏิบัติสันนิษฐานว่าจะต้องเหมือนกัน
underscore_d

@underscore_d: ใช่จุดยุติธรรม - unsigned long longรับประกันได้ว่ามีอย่างน้อย 64 บิต แต่ในทางทฤษฎีsize_tอาจมีขนาดใหญ่กว่านี้ฉันคิดว่า แน่นอนถ้าคุณมีองค์ประกอบ> 2 ^ 64 ในคอนเทนเนอร์ของคุณคุณอาจมีปัญหาที่ใหญ่กว่าที่ต้องกังวลเกี่ยวกับ ... ;-)
Paul R

1

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

void test (const int & a)
{
    // b is not const
    // b is not a reference

    auto b = a;

    // b type is decided by the compiler based on value of a
    // a is int
}

การใช้งานที่ดี

iterators

std::vector<boost::tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int> v();

..

std::vector<boost::tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int>::iterator it = v.begin();

// VS

auto vi = v.begin();

พอยน์เตอร์ฟังก์ชั่น

int test (ClassWithLongName1 a, ClassWithLongName2 b, int c)
{
    ..
}

..

int (*fp)(ClassWithLongName1, ClassWithLongName2, int) = test;

// VS

auto *f = test;

การใช้งานไม่ดี

การไหลของข้อมูล

auto input = "";

..

auto output = test(input);

ฟังก์ชั่นลายเซ็นต์

auto test (auto a, auto b, auto c)
{
    ..
}

กรณีเล็กน้อย

for(auto i = 0; i < 100; i++)
{
    ..
}

เมื่อคุณต้องการintคุณจะต้องพิมพ์อักขระอีกหนึ่งตัวถ้าคุณautoต้องการ นั่นเป็นสิ่งที่ยอมรับไม่ได้
Rerito

@Rerito ใช่มันเป็นintที่เห็นได้ง่ายและพิมพ์intได้สั้นกว่า นั่นเป็นเหตุผลว่าทำไมมันถึงเป็นเรื่องเล็กน้อย
Khaled.K

0

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

#include <iostream>
using namespace std;

int main() {
    auto n = 40;
    auto factorial = 1;

    for(int i = 1; i <=n; ++i)
    {
        factorial *= i;
    }

    cout << "Factorial of " << n << " = " << factorial <<endl;   
    cout << "Size of factorial: " << sizeof(factorial) << endl; 
    return 0;
}

รหัสนี้จะส่งออกนี้:

Factorial of 40 = 0
Size of factorial: 4

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

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