เหตุใด std :: initializer_list จึงไม่เป็นภาษาในตัว


97

เหตุใดจึงไม่มีstd::initializer_listภาษาหลักในตัว

สำหรับฉันแล้วดูเหมือนว่ามันเป็นคุณสมบัติที่สำคัญของ C ++ 11 แต่ก็ไม่มีคำหลักที่สงวนไว้ (หรือบางอย่างที่เหมือนกัน)

แต่initializer_listเป็นเพียงคลาสเทมเพลตจากไลบรารีมาตรฐานที่มีการแมปพิเศษโดยนัยจากไวยากรณ์รายการวงเล็บปีกกา ใหม่{...}ที่คอมไพเลอร์จัดการ

ตอนแรกคิดว่าโซลูชันนี้ค่อนข้างแฮ็

นี่เป็นวิธีการใช้งานส่วนเพิ่มเติมใหม่ในภาษา C ++: โดยบทบาทโดยปริยายของคลาสเทมเพลตบางคลาสไม่ใช่ภาษาหลักหรือไม่?


โปรดพิจารณาตัวอย่างเหล่านี้:

   widget<int> w = {1,2,3}; //this is how we want to use a class

เหตุใดจึงเลือกคลาสใหม่:

   widget( std::initializer_list<T> init )

แทนที่จะใช้สิ่งที่คล้ายกับแนวคิดเหล่านี้:

   widget( T[] init, int length )  // (1)
   widget( T... init )             // (2)
   widget( std::vector<T> init )   // (3)
  1. อาร์เรย์คลาสสิกคุณอาจเพิ่มตรงconstนี้และตรงนี้
  2. มีจุดสามจุดอยู่แล้วในภาษา (var-args ตอนนี้แม่แบบตัวแปร) ทำไมไม่ใช้ไวยากรณ์ซ้ำ (และทำให้รู้สึกว่ามีอยู่ในตัว )
  3. เพียงแค่คอนเทนเนอร์ที่มีอยู่สามารถเพิ่มconstและ&

ทั้งหมดนี้เป็นส่วนหนึ่งของภาษาอยู่แล้ว ผมเขียนเพียง 3 ความคิดแรกของฉันฉันแน่ใจว่ามีหลายวิธีการอื่น ๆ


26
คณะกรรมการมาตรฐานเกลียดการเพิ่มคีย์เวิร์ดใหม่!
Alex Chamberlain

11
สิ่งนี้ฉันเข้าใจ แต่มีความเป็นไปได้มากมายในการขยายภาษา ( คำหลักเป็นเพียงตัวอย่าง )
emesx

10
std::array<T>ไม่ได้เป็น 'ส่วนหนึ่งของภาษา' std::initializer_list<T>มากไปกว่า และสิ่งเหล่านี้ไม่ใช่ส่วนประกอบของไลบรารีเพียงอย่างเดียวที่ภาษาต้องอาศัย ดูnew/ delete, type_infoประเภทข้อยกเว้นต่างๆsize_tฯลฯ
bames53

6
@ เอล์มส์: ฉันอยากจะแนะนำconst T(*)[N]เพราะมันทำงานคล้ายกับวิธีการstd::initializer_listทำงานมาก
หมูปิ้ง

1
สิ่งนี้ตอบได้ว่าทำไมstd::arrayหรืออาร์เรย์ที่มีขนาดคงที่จึงเป็นทางเลือกที่ต้องการน้อยกว่า
boycy

คำตอบ:


48

มีตัวอย่างของคุณลักษณะภาษา "หลัก" ที่ส่งคืนประเภทที่กำหนดไว้ในstdเนมสเปซแล้ว typeidผลตอบแทนstd::type_infoและ (ยืดจุดอาจ) ผลตอบแทนsizeofstd::size_t

ในกรณีก่อนหน้านี้คุณต้องใส่ส่วนหัวมาตรฐานอยู่แล้วเพื่อที่จะใช้คุณลักษณะนี้ที่เรียกว่า "ภาษาหลัก"

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

ใช่คุณอาจคาดหวังหลักการออกแบบนี้ได้มากขึ้นในอนาคต:

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

ดังนั้น:

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

ฉันคิดว่าสิ่งที่กล่าวมาก็คือไม่มีการหารที่แน่นอนใน C ++ ระหว่าง "ภาษาหลัก" และไลบรารีมาตรฐาน เป็นบทที่แตกต่างกันในมาตรฐาน แต่แต่ละบทอ้างอิงอีกบทหนึ่งและเป็นเช่นนั้นมาโดยตลอด

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


1
สำหรับฉันแล้วดูเหมือนว่าการแบ่งนี้เป็นไปไม่ได้ (เมลนี่?) เนื่องจากบทบาทของประเภทโดยนัยดังกล่าว type_infoและsize_tเป็นข้อโต้แย้งที่ดี .. size_tเป็นเพียงตัวพิมพ์เล็ก .. ดังนั้นขอข้ามสิ่งนี้ไป ความแตกต่างระหว่างtype_infoและinitializer_listคือตัวแรกเป็นผลมาจากตัวดำเนินการที่ชัดเจนและตัวที่สองของการดำเนินการคอมไพเลอร์โดยปริยาย สำหรับฉันแล้วดูเหมือนว่าinitializer_list อาจถูกแทนที่ด้วยคอนเทนเนอร์ที่มีอยู่แล้วบางส่วน .. หรือดีกว่า: ผู้ใช้ใด ๆ ก็ตามที่ประกาศเป็นประเภทอาร์กิวเมนต์!
emesx

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

2
@ คริสเตียน: ไม่ไม่มีstd::arrayแม้แต่ผู้สร้างใด ๆ std::arrayกรณีเป็นเพียงการเริ่มต้นรวม- นอกจากนี้ฉันยินดีให้คุณเข้าร่วมในห้องสนทนา Lounge <C ++> เนื่องจากการสนทนานี้ค่อนข้างยาว
Xeo

3
@ChristianRau: Xeo หมายถึงองค์ประกอบจะถูกคัดลอกเมื่อสร้างรายการตัวเริ่มต้น การคัดลอกรายการเริ่มต้นจะไม่คัดลอกองค์ประกอบที่มีอยู่
หมูปิ้ง

2
@Christian List-initialisation ไม่ได้หมายความถึง initializer_list อาจเป็นได้หลายอย่างรวมถึงการเริ่มต้นโดยตรงของ ole ที่ดีหรือการเริ่มต้นรวม ไม่มีสิ่งใดที่เกี่ยวข้องกับ initializer_list (และบางส่วนก็ไม่สามารถทำงานได้เช่นนั้น)
R.Martinho Fernandes

42

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

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

การปฏิบัติต่อ an std::initializer_listเป็นคอนเทนเนอร์อื่น ๆ ทำให้คุณมีโอกาสเขียนโค้ดทั่วไปที่ใช้งานได้กับสิ่งเหล่านั้น

อัพเดท:

แล้วทำไมต้องแนะนำประเภทใหม่แทนที่จะใช้ชุดค่าผสมที่มีอยู่? (จากความคิดเห็น)

ในการเริ่มต้นคอนเทนเนอร์อื่น ๆ ทั้งหมดมีวิธีการในการเพิ่มการลบและการวางองค์ประกอบซึ่งไม่เป็นที่ต้องการสำหรับคอลเล็กชันที่สร้างโดยคอมไพเลอร์ ข้อยกเว้นเพียงอย่างเดียวคือการstd::array<>รวมอาร์เรย์สไตล์ C ที่มีขนาดคงที่ดังนั้นจึงเป็นเพียงตัวเลือกที่สมเหตุสมผล

อย่างไรก็ตามตามที่Nicol Bolasชี้ให้เห็นอย่างถูกต้องในความคิดเห็นข้อแตกต่างพื้นฐานอีกประการหนึ่งระหว่างstd::initializer_listและคอนเทนเนอร์มาตรฐานอื่น ๆ ทั้งหมด (รวมถึงstd::array<>) คือคอนเทนเนอร์หลังมีความหมายเชิงคุณค่าในขณะที่std::initializer_listมีความหมายอ้างอิง std::initializer_listตัวอย่างเช่นการคัดลอกจะไม่ทำให้เกิดสำเนาขององค์ประกอบที่มีอยู่

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


4
แล้วทำไมต้องแนะนำประเภทใหม่แทนที่จะใช้ชุดค่าผสมที่มีอยู่?
emesx

3
@elmes: จริงๆแล้วมันเป็นเช่นstd::arrayนั้นมากกว่า แต่std::arrayจัดสรรหน่วยความจำในขณะที่std::initializaer_listรวมอาร์เรย์เวลาคอมไพล์ คิดว่ามันเป็นความแตกต่างระหว่างและchar s[] = "array"; char *s = "initializer_list";
rodrigo

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

2
@rodrigo: std::arrayไม่ได้จัดสรรหน่วยความจำใด ๆ มันเป็นเรื่องธรรมดาT arr[N];สิ่งเดียวกับที่สำรองstd::initializer_listไว้
Xeo

6
@Xeo: T arr[N] ไม่จัดสรรหน่วยความจำอาจจะไม่ได้อยู่ในกองแบบไดนามิก แต่ที่อื่น ๆ ... std::arrayดังนั้นไม่ อย่างไรก็ตามผู้ใช้ไม่initializer_listสามารถสร้าง non-empty ได้ดังนั้นจึงไม่สามารถจัดสรรหน่วยความจำได้อย่างชัดเจน
rodrigo

6

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


2
วิธีการในชั้นเรียนหรือแบบสแตนด์อโลนbeginและendวิธีการ นี่คือ IMO ที่แตกต่างกันเล็กน้อย
emesx

3
ใช่ไหม? อีกครั้งคุณมีโครงสร้างภาษาบริสุทธิ์โดยอาศัยโครงสร้างเฉพาะของรหัสของคุณ นอกจากนี้ยังอาจทำได้โดยการแนะนำคำหลักใหม่เช่นiterable class MyClass { };
Spook

แต่คุณสามารถวางวิธีการได้ทุกที่ที่คุณต้องการนำไปใช้ตามที่คุณต้องการ .. มีความคล้ายคลึงกันฉันเห็นด้วย! คำถามนี้เกี่ยวกับinitializer_listว่า
emesx

4

นี่ไม่ใช่เรื่องใหม่แน่นอนและมีกี่คนที่ชี้ให้เห็นการปฏิบัตินี้มีอยู่ใน C ++ และอยู่ที่นั่นพูดใน C #

Andrei Alexandrescu ได้กล่าวถึงประเด็นที่ดีเกี่ยวกับเรื่องนี้: คุณอาจคิดว่ามันเป็นส่วนหนึ่งของเนมสเปซ "หลัก" ในจินตนาการแล้วมันจะเข้าท่ากว่า

ดังนั้นจึงเป็นจริงสิ่งที่ชอบ: core::initializer_list, core::size_t, core::begin(), core::end()และอื่น ๆ นี่เป็นเพียงความบังเอิญที่โชคร้ายที่stdเนมสเปซมีโครงสร้างภาษาหลักอยู่ภายใน


2

ไม่เพียง แต่สามารถทำงานได้อย่างสมบูรณ์ในไลบรารีมาตรฐานเท่านั้น การรวมไว้ในไลบรารีมาตรฐานไม่ได้หมายความว่าคอมไพเลอร์ไม่สามารถเล่นกลเม็ดที่ชาญฉลาดได้

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

กล่าวอีกนัยหนึ่งint i {5};สามารถเทียบเท่ากับint i(5);หรือint i=5;หรือแม้กระทั่งคลาส wrapper แบบง่ายintwrapper iw {5};อยู่ที่ไหนintwrapperใน int ที่มีตัวสร้างเล็กน้อยโดยใช้initializer_list


เรามีตัวอย่างที่ทำซ้ำได้ของคอมไพเลอร์ที่เล่น "เทคนิคฉลาด" แบบนี้หรือไม่? มันหมายถึงเหตุผลภายใต้as-ifแต่ฉันต้องการเห็นการพิสูจน์
underscore_d

แนวคิดในการเพิ่มประสิทธิภาพคอมไพเลอร์คือคอมไพลเลอร์สามารถแปลงโค้ดเป็นโค้ดที่เทียบเท่าได้ โดยเฉพาะอย่างยิ่ง C ++ อาศัยการเพิ่มประสิทธิภาพสำหรับนามธรรมที่ "ฟรี" แนวคิดในการแทนที่โค้ดจากไลบรารีมาตรฐานเป็นเรื่องปกติ (ดูที่รายการ gcc builtin gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html )
Paul de Vrieze

ในความเป็นจริงความคิดของคุณที่int i {5}เกี่ยวข้องกับสิ่งใด ๆstd::initializer_listนั้นไม่ถูกต้อง intไม่มีการใช้ตัวสร้างstd::initializer_listดังนั้นจึง5ใช้เพียงเพื่อสร้างมันโดยตรง ดังนั้นตัวอย่างหลักจึงไม่เกี่ยวข้อง ไม่มีการปรับให้เหมาะสมที่สุด นอกเหนือจากนั้นเนื่องจากstd::initializer_listเกี่ยวข้องกับคอมไพเลอร์ที่สร้างและพร็อกซีอาร์เรย์ 'จินตภาพ' ฉันเดาว่ามันน่าจะชอบการปรับให้เหมาะสม แต่นั่นคือส่วน 'เวทย์มนตร์' ในคอมไพเลอร์ดังนั้นจึงแยกออกจากกันว่าตัวเพิ่มประสิทธิภาพโดยทั่วไปสามารถทำอะไรก็ได้อย่างชาญฉลาดกับพริตตี้ วัตถุทื่อที่มีตัวทำซ้ำ 2 ตัวซึ่งผลลัพธ์
underscore_d

1

มันไม่ได้เป็นส่วนหนึ่งของภาษาหลักเพราะมันสามารถดำเนินการได้อย่างสิ้นเชิงในห้องสมุดเพียงเส้นและoperator new operator deleteจะมีข้อได้เปรียบอะไรในการทำให้คอมไพเลอร์ซับซ้อนขึ้นเพื่อสร้างมันขึ้นมา?

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