ประเภทการปฏิเสธ
ในภาษาโปรแกรมC++ คำ ว่าdecltype (ย่อมาจาก "declared type") เป็นคำ หลัก ที่ใช้ในการตรวจสอบชนิดของนิพจน์โดยเริ่มใช้ในC++11และมีจุดประสงค์หลักในการใช้งานด้านการเขียนโปรแกรมแบบเจเนริก (generic programming)ซึ่งมักเป็นเรื่องยากหรือแทบเป็นไปไม่ได้เลยที่จะแสดงชนิดข้อมูลที่ขึ้นอยู่กับพารามิเตอร์ ของเทมเพลต
เมื่อ เทคนิค การเขียนโปรแกรมแบบเจเนริกได้รับความนิยมมากขึ้นเรื่อยๆ ตลอดช่วงทศวรรษ 1990 ความจำเป็นสำหรับกลไกการอนุมานประเภทจึงได้รับการยอมรับ ผู้ผลิตคอมไพเลอร์หลายรายได้นำตัวดำเนินการเวอร์ชันของตนเองมาใช้ ซึ่งโดยทั่วไปเรียกว่าtypeofและมีการพัฒนาการใช้งานแบบพกพาที่มีฟังก์ชันการทำงานจำกัด โดยอาศัยคุณสมบัติของภาษาที่มีอยู่ ในปี 2002 Bjarne Stroustrupได้เสนอให้เพิ่มตัวดำเนินการเวอร์ชันมาตรฐานลงในภาษา C++ และแนะนำชื่อ "decltype" เพื่อสะท้อนให้เห็นว่าตัวดำเนินการจะให้ผลลัพธ์เป็น "ประเภทที่ประกาศ" ของนิพจน์
decltypeความหมายของได้รับการออกแบบมาเพื่อรองรับทั้งผู้เขียนไลบรารีทั่วไปและโปรแกรมเมอร์มือใหม่ โดยทั่วไป ประเภทที่อนุมานได้จะตรงกับประเภทของวัตถุหรือฟังก์ชันตามที่ประกาศไว้ในซอร์สโค้ด ทุกประการ เช่นเดียวกับตัวดำเนินการsizeof[ 1 ]decltypeตัวถูกดำเนินการของจะไม่ถูกประเมิน
คุณสมบัติที่เกี่ยวข้องคือ^^ตัวดำเนินการ (แนะนำในC++26 ) ใช้เพื่อเรียก ค่า การสะท้อน (เมตาเดตาประเภท) ของสัญลักษณ์ใดๆ[ 2 ]
แรงจูงใจ
ด้วยการนำเทมเพลต มาใช้ ในภาษาการเขียนโปรแกรม C++และการเกิดขึ้นของเทคนิคการเขียนโปรแกรมทั่วไป ที่ริเริ่มโดย Standard Template Libraryทำให้เกิดความต้องการกลไกในการรับประเภทของนิพจน์ซึ่งโดยทั่วไปเรียกว่าtypeofในการเขียนโปรแกรมทั่วไป มักจะยากหรือเป็นไปไม่ได้ที่จะแสดงประเภทที่ขึ้นอยู่กับพารามิเตอร์เทมเพลต[ 3 ] [ 4 ]โดยเฉพาะอย่างยิ่งประเภทการส่งคืนของอินสแตนซ์เทมเพลตฟังก์ชัน[ 3 ]
ผู้จำหน่ายหลายรายจัดเตรียมตัวtypeofดำเนินการเป็นส่วนขยายของคอมไพเลอร์[ 5 ]ย้อนกลับไปในปี 1997 ก่อนที่ C++ จะได้รับการกำหนดมาตรฐานอย่างสมบูรณ์ Brian Parker ได้เสนอวิธีแก้ปัญหาแบบพกพาโดยอิงจากsizeofตัวดำเนินการ[ 5 ]งานของเขาได้รับการขยายความโดย Bill Gibbons ซึ่งสรุปว่าเทคนิคนี้มีข้อจำกัดหลายประการและโดยทั่วไปแล้วมีประสิทธิภาพน้อยกว่าtypeofกลไก จริง [ 5 ]ในบทความเดือนตุลาคมปี 2000 ของDr. Dobb's Journal Andrei Alexandrescuได้กล่าวว่า "การมี typeof จะทำให้การเขียนและทำความเข้าใจโค้ดเทมเพลตง่ายขึ้นมาก" [ 6 ]เขายังตั้งข้อสังเกตอีกว่า "typeof และ sizeof ใช้แบ็กเอนด์เดียวกัน เนื่องจาก sizeof ต้องคำนวณประเภทอยู่แล้ว" [ 6 ] Andrew Koenigและ Barbara E. Moo ก็ตระหนักถึงประโยชน์ของสิ่งอำนวยความสะดวกในตัว เช่นกัน typeofโดยมีข้อแม้ว่า "การใช้งานมักจะนำไปสู่ข้อผิดพลาดในการเขียนโปรแกรมที่ละเอียดอ่อน และมีปัญหาบางอย่างที่มันไม่สามารถแก้ไขได้" [ 7 ]พวกเขาอธิบายการใช้ข้อกำหนดประเภท เช่นtypedefsที่จัดเตรียมโดยStandard Template Libraryว่าเป็นเทคนิคที่มีประสิทธิภาพและทั่วไปมากกว่า[ 7 ]อย่างไรก็ตาม Steve Dewhurst โต้แย้งว่าข้อกำหนดดังกล่าว "มีค่าใช้จ่ายสูงในการออกแบบและเผยแพร่" และ "จะง่ายกว่ามากหาก...เพียงแค่ดึงประเภทของนิพจน์ออกมา" [ 8 ]ในบทความปี 2011 บนC++0x Koenig และ Moo ทำนายว่า "decltype จะถูกนำมาใช้อย่างแพร่หลายเพื่อทำให้การเขียนโปรแกรมในชีวิตประจำวันง่ายขึ้น" [ 9 ]
ในปี 2002 Bjarne Stroustrupเสนอให้ขยายภาษา C++ ด้วยกลไกสำหรับการสอบถามประเภทของนิพจน์ และการเริ่มต้นวัตถุโดยไม่ต้องระบุประเภท[ 3 ] Stroustrup สังเกตว่าความหมายของการละทิ้งการอ้างอิงที่นำเสนอโดยtypeofตัวดำเนินการที่จัดทำโดย คอมไพเลอร์ GCCและEDGอาจเป็นปัญหา[ 3 ]ในทางกลับกัน ตัวดำเนินการที่ส่งคืนประเภทอ้างอิงตามค่า lvalueของนิพจน์นั้นถือว่าสับสนเกินไป ข้อเสนอเริ่มต้นต่อคณะกรรมการมาตรฐาน C++ ได้ร่างการผสมผสานของทั้งสองแบบ ตัวดำเนินการจะส่งคืนประเภทอ้างอิงก็ต่อเมื่อประเภทที่ประกาศของนิพจน์รวมถึงการอ้างอิง เพื่อเน้นว่าประเภทที่อนุมานจะสะท้อนถึง "ประเภทที่ประกาศ" ของนิพจน์ จึงมีการเสนอให้ตั้งชื่อตัวดำเนินการdecltypeว่า[ 3 ]
หนึ่งในแรงจูงใจหลักที่อ้างถึงสำหรับdecltypeข้อเสนอนี้คือความสามารถในการเขียนเทมเพลตฟังก์ชันการส่งต่อ ที่สมบูรณ์แบบ [ 10 ]บางครั้งเป็นที่พึงปรารถนาที่จะเขียนฟังก์ชันการส่งต่อทั่วไปที่ส่งคืนประเภทเดียวกันกับฟังก์ชันที่ห่อหุ้ม โดยไม่คำนึงถึงประเภทที่ใช้สร้างอินสแตนซ์ หากไม่มีสิ่งdecltypeนี้ โดยทั่วไปแล้วจะไม่สามารถทำเช่นนี้ได้[ 10 ]ตัวอย่างที่ใช้trailing-return-type ด้วย : [ 10 ]
int & foo ( int & i ) { ++ i ; return i ; }float foo ( float & f ) { f += 1.0f ; return f ; }template < class T > auto forwarder ( T & t ) − > decltype ( foo ( t )) { return foo ( t ); }decltypeมีความสำคัญในที่นี้เพราะช่วยรักษาข้อมูลเกี่ยวกับว่าฟังก์ชันที่ห่อหุ้มจะส่งคืนประเภทอ้างอิงหรือไม่[ 11 ]
ความหมาย
เช่นเดียวกับsizeofตัวดำเนินการ ตัวถูกดำเนินการของdecltypeจะไม่ถูกประเมิน ดังนั้นนิพจน์เช่นdecltype(i++)จะไม่ส่งผลให้ตัวแปร i เพิ่มขึ้น[ 12 ]โดยไม่เป็นทางการ ประเภทที่ส่งคืนโดยdecltype(e)จะถูกอนุมานดังนี้: [ 3 ]
- หากนิพจน์
eอ้างถึงตัวแปรในขอบเขตโลคอลหรือเนมสเปซตัวแปรสมาชิกแบบ สแตติก หรือพารามิเตอร์ของฟังก์ชัน ผลลัพธ์ที่ได้จะเป็น ประเภทที่ประกาศไว้ของตัวแปรหรือพารามิเตอร์นั้น - มิฉะนั้น ถ้า
eเป็นค่า lvalueผลลัพธ์decltype(e)จะเป็นT&โดยที่Tคือชนิดของ e; ถ้า e เป็นค่าxvalueผลลัพธ์จะเป็นT&&มิฉะนั้น e จะเป็นค่า prvalueและผลลัพธ์จะTเป็น - ในกรณีพิเศษ
decltype(auto)อนุญาตให้มีการอนุมานประเภทได้เช่นเดียวกับautoแต่จะรักษาหมวดหมู่ค่าของตัวเริ่มต้นไว้ โดยเฉพาะอย่างยิ่ง มันเทียบเท่ากับdecltype(initializer)
ความหมายเหล่านี้ได้รับการออกแบบมาเพื่อตอบสนองความต้องการของผู้เขียนไลบรารีทั่วไป ในขณะเดียวกันก็ใช้งานง่ายสำหรับโปรแกรมเมอร์มือใหม่ เนื่องจากประเภทการส่งคืนdecltypeจะตรงกับประเภทของวัตถุหรือฟังก์ชันที่ประกาศไว้ในซอร์สโค้ดเสมอ[ 3 ]ในทางที่เป็นทางการมากขึ้น กฎข้อที่ 1 ใช้กับนิพจน์ id ที่ไม่มีวงเล็บ และนิพจน์การเข้าถึงสมาชิกคลาส[ 13 ] [ 14 ]ตัวอย่าง: [ 13 ] หมายเหตุสำหรับบรรทัดที่เพิ่มสำหรับ bar() ด้านล่าง ประเภทที่อนุมานสำหรับ "bar()" คือ int ธรรมดา ไม่ใช่ const int เนื่องจาก prvalue ของประเภทที่ไม่ใช่คลาสจะมีประเภท cv ที่ไม่มีคุณสมบัติเสมอ แม้ว่าจะมีการประกาศประเภทที่แตกต่างกันแบบคงที่ก็ตาม
const int & foo () { static int val = 1 ; return val ; }const int bar () { return 1 ; }int i = 0 ;struct Double { double v ; };const Double * d = new Double ();decltype ( foo ()) x1 ; // ชนิดคือ const int& decltype ( bar ()) x2 ; // ชนิดคือ int decltype ( i ) x3 ; // ชนิดคือ int decltype ( d -> i ) x4 ; // ชนิดคือ double decltype (( d -> i )) x5 ; // ชนิดคือ const double&ลบd ;เหตุผลของความแตกต่างระหว่างการเรียกใช้สองครั้งหลังdecltypeคือ นิพจน์ที่อยู่ในวงเล็บนั้น(a->x)ไม่ใช่ทั้งนิพจน์ระบุตัวตนหรือนิพจน์การเข้าถึงสมาชิก ดังนั้นจึงไม่แสดงถึงวัตถุที่มีชื่อ[ 15 ]เนื่องจากนิพจน์เป็น lvalue ประเภทที่อนุมานได้จึงเป็น "การอ้างอิงถึงประเภทของนิพจน์" หรือconst double&[ 12 ] ข้อเท็จจริง ที่ว่าวงเล็บพิเศษแนะนำตัวระบุการอ้างอิงให้กับประเภทอาจเป็นแหล่งที่มาของข้อผิดพลาดสำหรับโปรแกรมเมอร์ที่ไม่เข้าใจอย่างdecltypeถ่องแท้[ 16 ]
ในเดือนธันวาคม พ.ศ. 2551 Jaakko Järvi ได้หยิบยกข้อกังวลต่อคณะกรรมการเกี่ยวกับการไม่สามารถใช้decltypeเพื่อสร้างqualified-id [ 1 ]ซึ่งไม่สอดคล้องกับเจตนาที่ควรได้รับการปฏิบัติ "ราวกับว่าเป็นtypedef-name " [ 17 ]ในขณะที่แสดงความคิดเห็นเกี่ยวกับร่างคณะกรรมการอย่างเป็นทางการสำหรับC++0x หน่วยงานสมาชิก ISOของญี่ปุ่นได้ตั้งข้อสังเกตว่า "ตัวดำเนินการขอบเขต (::) ไม่สามารถใช้กับ decltype ได้ แต่ควรจะใช้ได้ ในกรณีดังกล่าว การรับประเภทสมาชิก (nested-type) จากอินสแตนซ์จะมีประโยชน์ดังนี้: [ 18 ]decltype(e)
vector < int > v ; decltype ( v ) :: value_type i = 0 ; // int i = 0;ประเด็นนี้และประเด็นที่คล้ายคลึงกันเกี่ยวกับถ้อยคำที่ขัดขวางการใช้decltypeในการประกาศคลาสที่สืบทอดและใน การเรียก ตัวทำลายได้รับการแก้ไขโดย David Vandevoorde และได้รับการลงมติให้รวมอยู่ในเอกสารการทำงานในเดือนมีนาคม 2010 [ 19 ] [ 20 ]
ความพร้อมใช้งาน
decltypeรวมอยู่ในมาตรฐานภาษา C++ ตั้งแต่C++11 [ 13 ] มีให้โดยคอมไพเลอร์หลายตัวเป็นส่วนขยาย คอมไพ เลอร์ Visual C++ 2010และเวอร์ชันต่อมาของMicrosoft มี ตัวระบุประเภทที่เลียนแบบความหมายอย่างใกล้ชิดตามที่อธิบายไว้ในข้อเสนอของคณะกรรมการมาตรฐาน สามารถใช้ได้ทั้งกับ โค้ด จัดการและโค้ดเนทีฟ[ 11 ]เอกสารระบุว่า "มีประโยชน์หลักๆ สำหรับนักพัฒนาที่เขียนไลบรารีเทมเพลต" [ 11 ]ถูกเพิ่มเข้าไปในสายหลักของ คอมไพเลอร์ GCC C++ เวอร์ชัน 4.3 [ 21 ]ซึ่งวางจำหน่ายเมื่อวันที่ 5 มีนาคม 2008 [ 22 ] นอกจากนี้ยังมีอยู่ในCodegear ' s C++ Builder 2009 [ 23 ] Intel C++ Compiler [ 24 ]และClang [ 25 ]decltypedecltypedecltype
ลิงก์ภายนอก
- บทความ FAQ ของ Bjarne Stroustrup เกี่ยวกับ C++0x สำหรับ decltype