การใช้งานส่วนใหญ่ของยาชื่อสามัญ (หรือมากกว่า: ตัวแปรความหลากหลาย) ใช้ลบประเภท สิ่งนี้ช่วยลดความยุ่งยากในการรวบรวมรหัสทั่วไป แต่ใช้ได้กับกล่องที่พิมพ์เท่านั้น: เนื่องจากอาร์กิวเมนต์แต่ละตัวเป็นตัวชี้ทึบแสงได้อย่างมีประสิทธิภาพเราจึงจำเป็นต้องมี VTable หรือกลไกการจัดส่งที่คล้ายกันเพื่อดำเนินการกับอาร์กิวเมนต์ ใน Java:
<T extends Addable> T add(T a, T b) { … }
สามารถคอมไพล์ตรวจสอบชนิดและเรียกว่าแบบเดียวกับ
Addable add(Addable a, Addable b) { … }
ยกเว้นข้อมูลทั่วไปที่ให้ข้อมูลตัวตรวจสอบชนิดกับข้อมูลเพิ่มเติมที่ไซต์การโทร ข้อมูลพิเศษนี้สามารถจัดการกับตัวแปรประเภทโดยเฉพาะอย่างยิ่งเมื่ออนุมานประเภททั่วไป ในระหว่างการตรวจสอบประเภทประเภททั่วไปแต่ละประเภทสามารถถูกแทนที่ด้วยตัวแปรลองเรียกมันว่า$T1
:
$T1 add($T1 a, $T1 b)
จากนั้นตัวแปรประเภทนั้นจะได้รับการอัปเดตด้วยข้อเท็จจริงมากขึ้นเรื่อย ๆ จนกว่าจะสามารถถูกแทนที่ด้วยประเภทที่เป็นรูปธรรม อัลกอริทึมการตรวจสอบประเภทจะต้องเขียนในลักษณะที่รองรับตัวแปรประเภทเหล่านี้แม้ว่าพวกเขาจะยังไม่ได้รับการแก้ไขให้เป็นประเภทที่สมบูรณ์ ในจาวาตัวเองสิ่งนี้สามารถทำได้ง่าย ๆ เพราะมักจะรู้ชนิดของการโต้เถียงก่อนที่จะต้องรู้ประเภทของการเรียกใช้ฟังก์ชัน ข้อยกเว้นที่น่าสังเกตคือนิพจน์แลมบ์ดาเป็นอาร์กิวเมนต์ของฟังก์ชันซึ่งจำเป็นต้องใช้ตัวแปรประเภทดังกล่าว
ในภายหลังเครื่องมือเพิ่มประสิทธิภาพอาจสร้างโค้ดพิเศษสำหรับชุดอาร์กิวเมนต์บางชุดซึ่งจะเป็นการอินไลน์ชนิดที่มีประสิทธิภาพ
VTable สำหรับอาร์กิวเมนต์ที่พิมพ์ทั่วไปสามารถหลีกเลี่ยงได้หากฟังก์ชันทั่วไปไม่ได้ทำการดำเนินการใด ๆ กับประเภท แต่จะส่งผ่านไปยังฟังก์ชันอื่นเท่านั้น เช่นฟังก์ชัน Haskell call :: (a -> b) -> a -> b; call f x = f x
จะไม่ต้องใส่กล่องx
อาร์กิวเมนต์ อย่างไรก็ตามสิ่งนี้จะต้องมีการประชุมที่เรียกว่าสามารถผ่านค่าโดยไม่ทราบขนาดของพวกเขาซึ่งเป็นหลัก จำกัด ไว้ที่ตัวชี้ต่อไป
C ++ นั้นแตกต่างจากภาษาส่วนใหญ่ในแง่นี้ คลาสหรือฟังก์ชันของเทมเพลต (ฉันจะพูดถึงฟังก์ชั่นเทมเพลตที่นี่เท่านั้น) ไม่สามารถเรียกได้ในตัวเอง ควรเข้าใจเทมเพลตเป็นฟังก์ชันเมตาเวลารวบรวมที่ส่งคืนฟังก์ชันจริง ไม่สนใจการอนุมานอาร์กิวเมนต์เทมเพลตสักครู่วิธีการทั่วไปจากนั้นก็ทำตามขั้นตอนเหล่านี้:
ใช้เทมเพลตกับอาร์กิวเมนต์เท็มเพลตที่จัดเตรียม เช่นเรียกtemplate<class T> T add(T a, T b) { … }
เป็นadd<int>(1, 2)
จะให้เราฟังก์ชั่นที่เกิดขึ้นจริงint __add__T_int(int a, int b)
(หรือสิ่งที่ชื่อ mangling วิธีการจะใช้)
หากรหัสสำหรับฟังก์ชั่นนั้นได้ถูกสร้างขึ้นในหน่วยรวบรวมปัจจุบันแล้วให้ทำต่อ มิฉะนั้นสร้างรหัสราวกับว่าฟังก์ชั่นint __add__T_int(int a, int b) { … }
ได้รับการเขียนในรหัสที่มา สิ่งนี้เกี่ยวข้องกับการแทนที่อาร์กิวเมนต์เท็มเพลตทั้งหมดที่เกิดขึ้นด้วยค่าของมัน นี่อาจเป็นการแปลง AST → AST จากนั้นทำการตรวจสอบชนิดบน AST ที่สร้างขึ้น
__add__T_int(1, 2)
รวบรวมโทรราวกับว่ารหัสที่มาได้รับ
โปรดทราบว่าเท็มเพลต C ++ มีการโต้ตอบที่ซับซ้อนกับกลไกการแก้ปัญหาโอเวอร์โหลดซึ่งฉันไม่ต้องการอธิบายที่นี่ โปรดทราบว่าการสร้างโค้ดนี้ทำให้เป็นไปไม่ได้ที่จะมีวิธีการ templated ที่เป็นเสมือน - วิธีการลบประเภทที่ไม่ได้รับผลกระทบจากข้อ จำกัด ที่สำคัญนี้
สิ่งนี้หมายความว่าอย่างไรสำหรับคอมไพเลอร์และ / หรือภาษาของคุณ? คุณต้องคิดให้รอบคอบเกี่ยวกับประเภทของยาชื่อสามัญที่คุณต้องการเสนอ การลบประเภทในกรณีที่ไม่มีการอนุมานประเภทเป็นวิธีที่ง่ายที่สุดหากคุณรองรับประเภทกล่อง เทมเพลตนั้นค่อนข้างเรียบง่าย แต่โดยทั่วไปแล้วจะเกี่ยวข้องกับชื่อ mangling และ (สำหรับการรวบรวมหลายหน่วย) การทำซ้ำอย่างมากในเอาต์พุตเนื่องจากเทมเพลตถูกสร้างอินสแตนซ์ที่ไซต์การโทรไม่ใช่ไซต์นิยาม
วิธีการที่คุณแสดงเป็นหลักคล้าย C ++ อย่างไรก็ตามคุณเก็บเทมเพลตเฉพาะ / อินสแตนซ์เป็น“ รุ่น” ของเทมเพลตหลัก นี่เป็นสิ่งที่ทำให้เข้าใจผิด: พวกเขาไม่ได้เป็นแนวคิดเดียวกันและอินสแตนซ์ที่แตกต่างกันของฟังก์ชั่นอาจมีประเภทที่แตกต่างกันอย่างดุเดือด สิ่งนี้จะทำให้สิ่งต่าง ๆ ซับซ้อนในระยะยาวหากคุณอนุญาตให้ใช้งานมากไป แต่คุณจะต้องมีแนวคิดเกี่ยวกับชุดโอเวอร์โหลดที่มีฟังก์ชั่นและเทมเพลตที่เป็นไปได้ทั้งหมดที่ใช้ชื่อร่วมกันแทน ยกเว้นการแก้ไขปัญหาการโอเวอร์โหลดคุณสามารถพิจารณาแม่แบบอินสแตนซ์ที่แตกต่างกันเพื่อแยกจากกันโดยสมบูรณ์