นี่คือมุมมอง / คุณลักษณะใน C ++ แม้ว่าเราจะไม่คิดว่าการอ้างอิงเป็นประเภท แต่อันที่จริงแล้วพวกเขา "นั่ง" ในระบบประเภท แม้ว่าสิ่งนี้จะดูอึดอัด (เนื่องจากเมื่อมีการใช้การอ้างอิงความหมายของการอ้างอิงจะเกิดขึ้นโดยอัตโนมัติและการอ้างอิง "ออกนอกเส้นทาง") มีเหตุผลที่สามารถป้องกันได้ว่าทำไมการอ้างอิงจึงถูกสร้างแบบจำลองในระบบประเภทแทนที่จะเป็นแอตทริบิวต์แยกต่างหากนอก ชนิด.
ประการแรกให้เราพิจารณาว่าไม่ใช่ทุกแอตทริบิวต์ของชื่อที่ประกาศจะต้องอยู่ในระบบประเภท จากภาษา C เรามี "คลาสพื้นที่เก็บข้อมูล" และ "การเชื่อมโยง" ชื่อสามารถนำมาใช้extern const int ri
โดยที่extern
ระบุคลาสหน่วยเก็บแบบคงที่และสถานะของการเชื่อมโยง ประเภทเป็นเพียงconst int
.
เห็นได้ชัดว่า C ++ รวบรวมแนวคิดที่ว่านิพจน์มีคุณลักษณะที่อยู่นอกระบบประเภท ขณะนี้ภาษามีแนวคิดเรื่อง "ระดับค่า" ซึ่งเป็นความพยายามที่จะจัดระเบียบแอตทริบิวต์ที่ไม่ใช่ประเภทที่มีจำนวนเพิ่มขึ้นซึ่งนิพจน์สามารถแสดงได้
การอ้างอิงยังเป็นประเภท ทำไม?
เคยมีการอธิบายไว้ในบทช่วยสอน C ++ ว่าการประกาศเช่นconst int &ri
แนะนำri
ว่ามีประเภทconst int
แต่อ้างอิงความหมาย ความหมายอ้างอิงนั้นไม่ใช่ประเภท; มันเป็นเพียงแอตทริบิวต์ชนิดหนึ่งที่บ่งบอกถึงความสัมพันธ์ที่ผิดปกติระหว่างชื่อและที่เก็บข้อมูล นอกจากนี้ความจริงที่ว่าการอ้างอิงไม่ใช่ประเภทถูกใช้เพื่อหาเหตุผลว่าทำไมคุณไม่สามารถสร้างประเภทตามการอ้างอิงได้แม้ว่าไวยากรณ์การสร้างประเภทจะอนุญาตก็ตาม ตัวอย่างเช่นอาร์เรย์หรือตัวชี้ไปยังการอ้างอิงไม่สามารถทำได้: const int &ari[5]
และconst int &*pri
และ
แต่ในความเป็นจริงการอ้างอิงเป็นประเภทดังนั้นจึงdecltype(ri)
ดึงโหนดประเภทการอ้างอิงบางอย่างซึ่งไม่มีเงื่อนไข คุณต้องลงมาผ่านโหนดนี้ในต้นไม้ชนิดเพื่อไปยังประเภทที่อยู่ภายใต้remove_reference
คุณต้องลงมาที่ผ่านมาโหนดนี้ในต้นไม้ประเภทที่จะได้รับการอ้างอิงกับประเภท
เมื่อคุณใช้ri
การอ้างอิงจะได้รับการแก้ไขอย่างโปร่งใสเพื่อให้ri
"มีลักษณะและความรู้สึกi
" และสามารถเรียกว่า "นามแฝง" ได้ ในระบบประเภทri
นั้นมีประเภทที่ " อ้างอิงถึง " const int
" หรือไม่
เหตุใดการอ้างอิงจึงเป็นประเภท
พิจารณาว่าหากการอ้างอิงไม่ใช่ประเภทฟังก์ชันเหล่านี้จะถือว่ามีประเภทเดียวกัน:
void foo(int);
void foo(int &);
นั่นไม่สามารถเป็นได้ด้วยเหตุผลที่ค่อนข้างชัดเจนในตัวเอง หากมีประเภทเดียวกันนั่นหมายความว่าการประกาศอย่างใดอย่างหนึ่งจะเหมาะสมกับคำจำกัดความอย่างใดอย่างหนึ่งและทุกๆ(int)
ฟังก์ชั่นจะต้องถูกสงสัยว่ามีการอ้างอิง
ในทำนองเดียวกันหากการอ้างอิงไม่ใช่ประเภทการประกาศคลาสทั้งสองนี้จะเทียบเท่า:
class foo {
int m;
};
class foo {
int &m;
};
มันจะถูกต้องสำหรับหน่วยการแปลหนึ่งที่จะใช้คำประกาศหนึ่งฉบับและอีกหน่วยการแปลในโปรแกรมเดียวกันเพื่อใช้คำประกาศอื่น
ความจริงก็คือการอ้างอิงแสดงถึงความแตกต่างในการนำไปใช้งานและเป็นไปไม่ได้ที่จะแยกสิ่งนั้นออกจากประเภทเนื่องจากประเภทใน C ++ เกี่ยวข้องกับการนำเอนทิตีไปใช้: "เค้าโครง" ของมันเป็นบิตเพื่อที่จะพูด หากฟังก์ชันสองฟังก์ชันมีประเภทเดียวกันสามารถเรียกใช้ด้วยรูปแบบการเรียกไบนารีเดียวกัน: ABI เหมือนกัน ถ้าโครงสร้างหรือคลาสสองแบบมีประเภทเดียวกันเลย์เอาต์จะเหมือนกันรวมทั้งความหมายของการเข้าถึงสมาชิกทั้งหมด การมีอยู่ของการอ้างอิงจะเปลี่ยนแง่มุมของประเภทเหล่านี้ดังนั้นจึงเป็นการตัดสินใจออกแบบที่ตรงไปตรงมาเพื่อรวมไว้ในระบบประเภท (อย่างไรก็ตามโปรดสังเกตการโต้เถียงที่นี่: สมาชิกของโครงสร้าง / คลาสสามารถเป็นได้static
ซึ่งจะเปลี่ยนการเป็นตัวแทน แต่นั่นยังไม่ใช่ประเภท!)
ดังนั้นการอ้างอิงจึงอยู่ในระบบประเภทเป็น "พลเมืองชั้นสอง" (ไม่ต่างจากฟังก์ชันและอาร์เรย์ใน ISO C) มีบางสิ่งที่เราไม่สามารถ "ทำได้" กับการอ้างอิงเช่นการประกาศตัวชี้ไปยังการอ้างอิงหรืออาร์เรย์ของสิ่งเหล่านี้ แต่นั่นไม่ได้หมายความว่าพวกเขาไม่ใช่ประเภท พวกเขาไม่ได้พิมพ์ในลักษณะที่เหมาะสม
ข้อ จำกัด ชั้นสองเหล่านี้ไม่จำเป็นทั้งหมด เนื่องจากมีโครงสร้างของการอ้างอิงอาจมีอาร์เรย์ของการอ้างอิง! เช่น
int x = 0, y = 0;
int &ar[2] = { x, y };
สิ่งนี้ไม่ได้ใช้ใน C ++ นั่นคือทั้งหมด ตัวชี้ไปยังการอ้างอิงไม่สมเหตุสมผลเลยเนื่องจากตัวชี้ที่ยกขึ้นจากการอ้างอิงจะไปที่วัตถุที่อ้างอิงเท่านั้น สาเหตุที่เป็นไปได้ว่าทำไมไม่มีอาร์เรย์ของการอ้างอิงคือคน C ++ ถือว่าอาร์เรย์เป็นคุณลักษณะระดับต่ำที่สืบทอดมาจาก C ซึ่งเสียในหลาย ๆ วิธีที่ไม่สามารถแก้ไขได้และพวกเขาไม่ต้องการสัมผัสอาร์เรย์เป็น พื้นฐานสำหรับสิ่งใหม่ ๆ อย่างไรก็ตามการมีอยู่ของอาร์เรย์ของการอ้างอิงจะทำให้เห็นตัวอย่างที่ชัดเจนว่าการอ้างอิงต้องเป็นประเภทอย่างไร
const
ประเภทที่ไม่มีคุณสมบัติ: พบได้ใน ISO C90 ด้วย!
คำตอบบางคำบ่งชี้ถึงข้อเท็จจริงที่ว่าการอ้างอิงไม่ได้ใช้const
คุณสมบัติ นั่นค่อนข้างเป็นปลาเฮอริ่งสีแดงเนื่องจากการประกาศconst int &ri = i
ไม่ได้พยายามที่จะทำการconst
อ้างอิงที่มีคุณสมบัติ: เป็นการอ้างอิงถึงประเภทที่มีคุณสมบัติเหมาะสมของ const (ซึ่งไม่ใช่ตัวมันเองconst
) เช่นเดียวกับการconst in *ri
ประกาศตัวชี้ไปยังบางสิ่งconst
แต่ตัวชี้นั้นไม่ใช่ตัวมันเองconst
แต่ชี้ว่าไม่เป็นตัวเอง
ที่กล่าวว่าเป็นเรื่องจริงที่การอ้างอิงไม่สามารถนำไฟล์ const
คัดเลือกตัวเองได้
แต่นี่ไม่ใช่เรื่องแปลกประหลาด แม้แต่ในภาษา ISO C 90 ก็ไม่สามารถทำได้ทุกประเภทconst
ภาษาไม่ได้ทุกประเภทสามารถ อาร์เรย์ไม่สามารถเป็นได้
ประการแรกไม่มีไวยากรณ์สำหรับการประกาศอาร์เรย์ const: int a const [42]
ผิดพลาด
อย่างไรก็ตามสิ่งที่คำประกาศข้างต้นพยายามจะทำสามารถแสดงออกผ่านสื่อกลางtypedef
:
typedef int array_t[42];
const array_t a;
แต่สิ่งนี้ไม่ได้ทำในสิ่งที่ดูเหมือน ในคำประกาศนี้ไม่ใช่a
สิ่งที่const
มีคุณสมบัติ แต่เป็นองค์ประกอบ! กล่าวa[0]
คือเป็น a const int
แต่a
เป็นเพียง "อาร์เรย์ของ int" ดังนั้นจึงไม่จำเป็นต้องมีการวินิจฉัย:
int *p = a;
สิ่งนี้ทำ:
a[0] = 1;
อีกครั้งสิ่งนี้เน้นย้ำถึงแนวคิดที่ว่าการอ้างอิงอยู่ในความหมายบางอย่าง "ชั้นสอง" ในระบบประเภทเช่นอาร์เรย์
สังเกตว่าการเปรียบเทียบนั้นมีความลึกซึ้งยิ่งขึ้นอย่างไรเนื่องจากอาร์เรย์มี "พฤติกรรมการแปลงที่มองไม่เห็น" เช่นการอ้างอิง โดยที่โปรแกรมเมอร์ไม่ต้องใช้ตัวดำเนินการที่ชัดเจนตัวระบุa
จะเปลี่ยนเป็นint *
ตัวชี้โดยอัตโนมัติราวกับว่า&a[0]
มีการใช้นิพจน์ สิ่งนี้คล้ายคลึงกับวิธีการอ้างอิงri
เมื่อเราใช้เป็นนิพจน์หลักหมายถึงวัตถุi
ที่ถูกผูกไว้อย่างน่าอัศจรรย์ มันเป็นเพียง "การสลายตัว" อื่น ๆ เช่น "array to pointer สลายตัว"
และเช่นเดียวกับที่เราต้องไม่สับสนกับ "array to pointer" โดยคิดผิด ๆ ว่า "อาร์เรย์เป็นเพียงตัวชี้ใน C และ C ++" เราก็ต้องไม่คิดว่าการอ้างอิงเป็นเพียงนามแฝงที่ไม่มีประเภทเป็นของตัวเอง
เมื่อdecltype(ri)
ระงับการแปลงตามปกติของการอ้างอิงไปยังออบเจ็กต์ที่อ้างอิงสิ่งนี้จะไม่แตกต่างจากsizeof a
การระงับการแปลงอาร์เรย์เป็นตัวชี้และดำเนินการกับชนิดอาร์เรย์เพื่อคำนวณขนาด
boolalpha(cout)
เป็นเรื่องผิดปกติมาก คุณสามารถทำstd::cout << boolalpha
แทนได้