เหตุใดการอ้างอิงจึงไม่ใช่ "const" ใน C ++


87

เราทราบว่า "ตัวแปร const" บ่งชี้ว่าเมื่อกำหนดแล้วคุณจะไม่สามารถเปลี่ยนตัวแปรได้เช่นนี้:

int const i = 1;
i = 2;

โปรแกรมด้านบนจะไม่สามารถคอมไพล์ได้ gcc แจ้งข้อผิดพลาด:

assignment of read-only variable 'i'

ไม่มีปัญหาฉันเข้าใจได้ แต่ตัวอย่างต่อไปนี้อยู่นอกเหนือความเข้าใจของฉัน:

#include<iostream>
using namespace std;
int main()
{
    boolalpha(cout);
    int const i = 1;
    cout << is_const<decltype(i)>::value << endl;
    int const &ri = i;
    cout << is_const<decltype(ri)>::value << endl;
    return 0;
}

มันส่งออก

true
false

แปลก. เรารู้ว่าเมื่อการอ้างอิงถูกผูกไว้กับชื่อ / ตัวแปรแล้วเราไม่สามารถเปลี่ยนการเชื่อมโยงนี้ได้เราจะเปลี่ยนอ็อบเจ็กต์ที่ถูกผูกไว้ ดังนั้นผมจึงคิดว่าประเภทของriที่ควรจะเป็นเช่นเดียวกับiเมื่อiเป็นint constทำไมriไม่const?


3
นอกจากนี้boolalpha(cout)เป็นเรื่องผิดปกติมาก คุณสามารถทำstd::cout << boolalphaแทนได้
isanae

6
มากสำหรับriการเป็น "สมญานาม" iแยกไม่ออกจาก
Kaz

1
เนื่องจากการอ้างอิงมักจะล้อมรอบวัตถุเดียวกัน iเป็นข้อมูลอ้างอิงเช่นกัน แต่ด้วยเหตุผลทางประวัติศาสตร์คุณไม่ได้ประกาศในลักษณะนี้อย่างชัดเจน ดังนั้นจึงiเป็นการอ้างอิงที่อ้างถึงที่จัดเก็บข้อมูลและriเป็นการอ้างอิงที่อ้างถึงที่เก็บข้อมูลเดียวกัน แต่มีความแตกต่างในธรรมชาติในระหว่างไม่มีและi ในขณะที่คุณไม่สามารถเปลี่ยนผลผูกพันของการอ้างอิงไม่มีความจำเป็นที่จะมีคุณสมบัติเป็นri constและให้ฉันอ้างว่าความคิดเห็น @Kaz นั้นดีกว่ามากว่าคำตอบที่ตรวจสอบแล้ว (อย่าอธิบายการอ้างอิงโดยใช้พอยน์เตอร์การอ้างอิงคือชื่อ ptr เป็นตัวแปร)
Jean-Baptiste Yunès

1
คำถามที่ยอดเยี่ยม ก่อนที่จะเห็นตัวอย่างนี้ฉันคาดว่าis_constจะกลับมาtrueในกรณีนี้ด้วย ในความคิดของฉันนี่เป็นตัวอย่างที่ดีว่าเหตุใดจึงconstถอยหลังโดยพื้นฐาน แอตทริบิวต์ "เปลี่ยนแปลงได้" (a la Rust's mut) ดูเหมือนว่าจะสอดคล้องกันมากขึ้น
Kyle Strand

1
บางทีชื่อเรื่องควรเปลี่ยนเป็น "ทำไมจึงเป็นis_const<int const &>::valueเท็จ" หรือคล้ายกัน; ฉันดิ้นรนที่จะเห็นความหมายของคำถามนอกเหนือจากการถามเกี่ยวกับพฤติกรรมของลักษณะประเภท
MM

คำตอบ:


53

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

สิ่งนี้ดูเหมือนจะเป็นเหตุผลสำหรับตัวชี้ :

int main()
{
    boolalpha(cout);

    int const i = 1;
    cout << is_const<decltype(i)>::value << endl;

    int const* ri = &i;
    cout << is_const<decltype(ri)>::value << endl;
}

เอาท์พุต:

true
false

นี่เป็นตรรกะเพราะเรารู้ว่ามันไม่ใช่วัตถุตัวชี้ที่เป็น const (สามารถทำให้ชี้ไปที่อื่นได้) เป็นวัตถุที่ถูกชี้ไป

ดังนั้นเราจึงเห็นได้อย่างถูกต้องconstnessของตัวชี้falseตัวเองกลับมาเป็น

หากเราต้องการสร้างตัวชี้เองconstเราต้องพูดว่า:

int main()
{
    boolalpha(cout);

    int const i = 1;
    cout << is_const<decltype(i)>::value << endl;

    int const* const ri = &i;
    cout << is_const<decltype(ri)>::value << endl;
}

เอาท์พุต:

true
true

และดังนั้นผมจึงคิดว่าเราเห็นความคล้ายคลึงประโยคที่มีการอ้างอิง

อย่างไรก็ตามการอ้างอิงที่มีความหมายแตกต่างกันเพื่อชี้เฉพาะอย่างยิ่งในหนึ่งเคารพที่สำคัญเราจะไม่ได้รับอนุญาตให้rebindอ้างอิงถึงวัตถุอื่นที่ครั้งหนึ่งเคยผูกพัน

ดังนั้นแม้ว่าการอ้างอิงแบ่งปันไวยากรณ์เช่นเดียวกับตัวชี้กฎระเบียบที่แตกต่างกันและเพื่อป้องกันไม่ให้ภาษาที่เราจากการประกาศการอ้างอิงตัวเองconstเช่นนี้

int main()
{
    boolalpha(cout);

    int const i = 1;
    cout << is_const<decltype(i)>::value << endl;

    int const& const ri = i; // COMPILE TIME ERROR!
    cout << is_const<decltype(ri)>::value << endl;
}

ฉันถือว่าเราไม่ได้รับอนุญาตให้ทำเช่นนี้เพราะดูเหมือนว่าจะไม่จำเป็นเมื่อกฎของภาษาป้องกันไม่ให้การอ้างอิงถูกตอบกลับในลักษณะเดียวกับที่ตัวชี้ทำได้ (หากไม่ได้ประกาศไว้const)

ดังนั้นเพื่อตอบคำถาม:

ถาม)เหตุใด "การอ้างอิง" จึงไม่ใช่ "const" ใน C ++

ในตัวอย่างของไวยากรณ์ทำให้สิ่งที่ถูกเรียกconstแบบเดียวกับที่มันจะถ้าคุณประกาศตัวชี้

ถูกหรือผิดเราจะไม่ได้รับอนุญาตที่จะทำให้การอ้างอิงตัวเองconstแต่ถ้าเรามันจะมีลักษณะเช่นนี้

int const& const ri = i; // not allowed

Q)เรารู้ว่าเมื่อการอ้างอิงเชื่อมโยงกับชื่อ / ตัวแปรเราไม่สามารถเปลี่ยนการเชื่อมโยงนี้ได้เราเปลี่ยนอ็อบเจ็กต์ที่ผูกไว้ ดังนั้นผมจึงคิดว่าประเภทของriที่ควรจะเป็นเช่นเดียวกับiเมื่อiเป็นint constทำไมriไม่const?

ทำไมdecltype()ไม่โอนไปยังวัตถุที่refereceถูกผูกไว้กับ?

ฉันคิดว่านี่คือการเทียบเคียงทางความหมายกับพอยน์เตอร์และบางทีฟังก์ชันของdecltype()(ประเภทที่ประกาศ) คือการย้อนกลับไปดูสิ่งที่ประกาศก่อนที่จะเกิดการรวม


13
ดูเหมือนว่าคุณกำลังพูดว่า "references can't be const because they always const"?
ruakh

2
มันอาจจะจริงก็ได้ "การกระทำทั้งหมดที่มีความหมายหลังจากที่พวกเขาถูกผูกไว้จะถูกโอนไปยังวัตถุที่พวกเขาอ้างถึง" แต่ OP คาดหวังว่าจะนำไปใช้decltypeด้วยเช่นกันและพบว่ามันไม่ได้
ruakh

10
มีข้อสันนิษฐานที่วกวนและการเปรียบเทียบที่เกี่ยวข้องกับพอยน์เตอร์ที่นี่อย่างน่าสงสัยซึ่งฉันคิดว่าทำให้ปัญหาสับสนเกินความจำเป็นเมื่อตรวจสอบมาตรฐานเช่น sonyuanyao ได้ตรงไปยังจุดที่แท้จริง: การอ้างอิงไม่ใช่ประเภทที่มีคุณสมบัติ CV ดังนั้น มันไม่สามารถconstจึงต้องกลับstd::is_const falseพวกเขาสามารถใช้ถ้อยคำแทนซึ่งหมายความว่าจะต้องกลับมาtrueแต่ก็ไม่ได้ แค่นั้นแหละ! ข้อมูลทั้งหมดนี้เกี่ยวกับพอยน์เตอร์ "ฉันถือว่า" "ฉันคิดว่า" ฯลฯ ไม่ได้ให้คำอธิบายที่แท้จริง
underscore_d

1
@underscore_d นี่อาจเป็นคำถามที่ดีกว่าสำหรับการแชทมากกว่าสำหรับส่วนความคิดเห็นที่นี่ แต่คุณมีตัวอย่าง "ความสับสนที่ไม่จำเป็น" จากวิธีคิดเกี่ยวกับการอ้างอิงนี้หรือไม่? หากสามารถนำไปปฏิบัติได้จะมีปัญหาอะไรในการคิดเช่นนั้น? (ฉันไม่คิดว่าคำถามนี้มีค่าเนื่องจากdecltypeไม่ใช่การดำเนินการรันไทม์ดังนั้นแนวคิด "ที่รันไทม์การอ้างอิงจะทำงานเหมือนพอยน์เตอร์" ไม่ว่าจะถูกต้องหรือไม่ก็ตาม
Kyle Strand

1
การอ้างอิงไม่ได้รับการปฏิบัติในรูปแบบวากยสัมพันธ์เช่นพอยน์เตอร์เช่นกัน พวกเขามีไวยากรณ์ที่แตกต่างกันในการประกาศและใช้ ฉันไม่แน่ใจด้วยซ้ำว่าคุณหมายถึงอะไร
Jordan Melo

55

ทำไม "ri" ไม่ "const"?

std::is_const ตรวจสอบว่าประเภทมีคุณสมบัติครบถ้วนหรือไม่

ถ้า T เป็นชนิดที่มีคุณสมบัติของ const (นั่นคือ const หรือ const ระเหยได้) ให้ค่าคงที่ของสมาชิกเท่ากับจริง สำหรับประเภทอื่น ๆ ค่าเป็นเท็จ

แต่การอ้างอิงไม่สามารถเป็นคุณสมบัติของ const ได้ เอกสารอ้างอิง [dcl.ref] / 1

การอ้างอิงที่ผ่านการรับรอง Cv นั้นมีรูปแบบที่ไม่ถูกต้องยกเว้นเมื่อมีการนำเสนอ cv-qualifiers ผ่านการใช้ typedef-name ([dcl.typedef], [temp.param]) หรือตัวระบุชนิด ([dcl.type.simple]) ซึ่งในกรณีนี้จะมีการละเว้น cv-qualifiers

ดังนั้นis_const<decltype(ri)>::valueจะส่งคืนfalseเนื่องจากri(การอ้างอิง) ไม่ใช่ประเภทที่มีคุณสมบัติเหมาะสมของ const ดังที่คุณกล่าวไปแล้วเราไม่สามารถเชื่อมโยงการอ้างอิงอีกครั้งหลังจากการเริ่มต้นซึ่งหมายความว่าการอ้างอิงเป็น "const" เสมอไปในทางกลับกันการอ้างอิงที่มีคุณสมบัติตามมาตรฐาน const หรือการอ้างอิงที่ไม่มีคุณสมบัติตามเงื่อนไขอาจไม่สมเหตุสมผล


5
ตรงไปที่มาตรฐานและตรงประเด็นมากกว่าการคาดเดาวาฟเฟิลทั้งหมดของคำตอบที่ยอมรับ
underscore_d

5
@underscore_d นี่เป็นคำตอบที่ดีจนกว่าคุณจะรู้ว่าฝ่ายปฏิบัติการถามว่าทำไม "เพราะมันพูดอย่างนั้น" ไม่ได้มีประโยชน์กับคำถามในแง่มุมนั้นอย่างแท้จริง
simpleuser

5
@ user9999999 คำตอบอื่น ๆ ก็ไม่ได้ตอบโจทย์ด้านนั้นเช่นกัน ถ้าอ้างอิงไม่สามารถดีดตัวขึ้นทำไมไม่is_constกลับมาtrue? คำตอบนั้นพยายามที่จะเปรียบเทียบว่าพอยน์เตอร์สามารถเลือกใช้ซ้ำได้อย่างไรในขณะที่การอ้างอิงไม่ได้ - และในการทำเช่นนั้นจะนำไปสู่ความขัดแย้งในตัวเองด้วยเหตุผลเดียวกัน ฉันไม่แน่ใจว่ามีคำอธิบายที่แท้จริงไม่ว่าจะด้วยวิธีใดนอกเหนือจากการตัดสินใจโดยพลการของผู้เขียนมาตรฐานและบางครั้งก็เป็นสิ่งที่ดีที่สุดที่เราสามารถหวังได้ ดังนั้นคำตอบนี้
underscore_d

2
อีกด้านที่สำคัญผมคิดว่าเป็นที่decltypeเป็นไม่ได้ฟังก์ชั่นและดังนั้นจึงทำงานโดยตรงเกี่ยวกับการอ้างอิงตัวเองมากกว่าที่อ้างต่อวัตถุ (บางทีอาจจะเกี่ยวข้องกับคำตอบ "การอ้างอิงโดยทั่วไปเป็นตัวชี้" แต่ฉันก็ยังคิดว่ามันเป็นส่วนหนึ่งของสิ่งที่ทำให้ตัวอย่างนี้สับสนและควรกล่าวถึงที่นี่)
Kyle Strand

3
เพื่ออธิบายเพิ่มเติมความคิดเห็นจาก @KyleStrand นี้ทำหน้าที่แตกต่างจากทั่วไปdecltype(name) decltype(expr)ดังนั้นสำหรับตัวอย่างเช่นdecltype(i)เป็นชนิดที่ประกาศiซึ่งเป็นconst intขณะที่จะเป็นdecltype((i)) int const &
Daniel Schepler

18

คุณต้องใช้std::remove_referenceเพื่อให้ได้คุณค่าที่คุณต้องการ

std::cout << std::is_const<std::remove_reference<decltype(ri)>::type>::value << std::endl;

สำหรับข้อมูลเพิ่มเติมโปรดดูที่โพสต์นี้


8

ทำไมแมโครไม่const? ฟังก์ชั่น? อักษร? ชื่อประเภท?

const สิ่งต่าง ๆ เป็นเพียงส่วนย่อยของสิ่งที่ไม่เปลี่ยนรูป

เนื่องจากประเภทการอ้างอิงเป็นเพียงประเภทนั้น - จึงอาจมีเหตุผลที่จะต้องใช้const-qualifier ทั้งหมดเพื่อความสมมาตรกับประเภทอื่น ๆ (โดยเฉพาะกับประเภทตัวชี้) แต่สิ่งนี้จะน่าเบื่ออย่างรวดเร็ว

หาก C ++ มีวัตถุที่ไม่เปลี่ยนรูปโดยค่าเริ่มต้นโดยต้องใช้mutableคีย์เวิร์ดในสิ่งที่คุณไม่ต้องการconstสิ่งนี้จะเป็นเรื่องง่ายเพียงแค่ไม่อนุญาตให้โปรแกรมเมอร์เพิ่มmutableประเภทการอ้างอิง

ตามที่เป็นอยู่พวกเขาไม่เปลี่ยนรูปโดยไม่มีคุณสมบัติ

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

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


6

นี่คือมุมมอง / คุณลักษณะใน 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) มีบางสิ่งที่เราไม่สามารถ "ทำได้" กับการอ้างอิงเช่นการประกาศตัวชี้ไปยังการอ้างอิงหรืออาร์เรย์ของสิ่งเหล่านี้ แต่นั่นไม่ได้หมายความว่าพวกเขาไม่ใช่ประเภท พวกเขาไม่ได้พิมพ์ในลักษณะที่เหมาะสม

ข้อ จำกัด ชั้นสองเหล่านี้ไม่จำเป็นทั้งหมด เนื่องจากมีโครงสร้างของการอ้างอิงอาจมีอาร์เรย์ของการอ้างอิง! เช่น

// fantasy syntax
int x = 0, y = 0;
int &ar[2] = { x, y };

// ar[0] is now an alias for x: could be useful!

สิ่งนี้ไม่ได้ใช้ใน 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; /* surprise! */

สิ่งนี้ทำ:

a[0] = 1;

อีกครั้งสิ่งนี้เน้นย้ำถึงแนวคิดที่ว่าการอ้างอิงอยู่ในความหมายบางอย่าง "ชั้นสอง" ในระบบประเภทเช่นอาร์เรย์

สังเกตว่าการเปรียบเทียบนั้นมีความลึกซึ้งยิ่งขึ้นอย่างไรเนื่องจากอาร์เรย์มี "พฤติกรรมการแปลงที่มองไม่เห็น" เช่นการอ้างอิง โดยที่โปรแกรมเมอร์ไม่ต้องใช้ตัวดำเนินการที่ชัดเจนตัวระบุaจะเปลี่ยนเป็นint *ตัวชี้โดยอัตโนมัติราวกับว่า&a[0]มีการใช้นิพจน์ สิ่งนี้คล้ายคลึงกับวิธีการอ้างอิงriเมื่อเราใช้เป็นนิพจน์หลักหมายถึงวัตถุiที่ถูกผูกไว้อย่างน่าอัศจรรย์ มันเป็นเพียง "การสลายตัว" อื่น ๆ เช่น "array to pointer สลายตัว"

และเช่นเดียวกับที่เราต้องไม่สับสนกับ "array to pointer" โดยคิดผิด ๆ ว่า "อาร์เรย์เป็นเพียงตัวชี้ใน C และ C ++" เราก็ต้องไม่คิดว่าการอ้างอิงเป็นเพียงนามแฝงที่ไม่มีประเภทเป็นของตัวเอง

เมื่อdecltype(ri)ระงับการแปลงตามปกติของการอ้างอิงไปยังออบเจ็กต์ที่อ้างอิงสิ่งนี้จะไม่แตกต่างจากsizeof aการระงับการแปลงอาร์เรย์เป็นตัวชี้และดำเนินการกับชนิดอาร์เรย์เพื่อคำนวณขนาด


คุณมีข้อมูลที่น่าสนใจและเป็นประโยชน์มากมายที่นี่ (+1) แต่จะ จำกัด มากหรือน้อยก็คือ "การอ้างอิงอยู่ในระบบประเภท" ซึ่งไม่ได้ตอบคำถามของ OP ทั้งหมด ระหว่างคำตอบนี้กับคำตอบของ @ songyuanyao ฉันว่ามีข้อมูลมากเกินพอที่จะเข้าใจสถานการณ์ แต่รู้สึกว่าเป็นภูมิหลังที่จำเป็นสำหรับคำตอบแทนที่จะเป็นคำตอบที่สมบูรณ์
Kyle Strand

1
นอกจากนี้ฉันคิดว่าประโยคนี้ควรค่าแก่การเน้น: "เมื่อคุณใช้ ri การอ้างอิงจะได้รับการแก้ไขอย่างโปร่งใส ... " ประเด็นสำคัญประการหนึ่ง (ที่ฉันได้พูดถึงในความคิดเห็น แต่ยังไม่ปรากฏในคำตอบใด ๆ ) คือdecltype ไม่ได้ดำเนินการแก้ปัญหาแบบโปร่งใสนี้ (ไม่ใช่ฟังก์ชันดังนั้นจึงriไม่ "ใช้" ในความหมายที่คุณอธิบาย) พอดีในเรื่องนี้อย่างมากที่มีความสำคัญทั้งในระบบการพิมพ์ - พวกเขาเชื่อมต่อที่สำคัญคือdecltypeเป็นการดำเนินการประเภทระบบ
ไคล์สแตรนด์

อืมสิ่งที่เกี่ยวกับอาร์เรย์ให้ความรู้สึกเหมือนแทนเจนต์ แต่ประโยคสุดท้ายมีประโยชน์
Kyle Strand

..... แม้ว่า ณ ตอนนี้ฉันได้โหวตให้คุณแล้ว แต่ฉันไม่ใช่ OP ดังนั้นคุณจึงไม่ได้รับหรือสูญเสียอะไรเลยจากการฟังคำวิจารณ์ของฉัน ....
Kyle Strand

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

-6

const X & x” หมายถึง x นามแฝงวัตถุ X แต่คุณไม่สามารถเปลี่ยนวัตถุ X ผ่าน x ได้

และดูมาตรฐาน :: is_const


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