ข้อผิดพลาด: การเริ่มต้นที่ไม่ถูกต้องของการอ้างอิงที่ไม่ใช่ const ของประเภท 'int &' จาก rvalue ของประเภท 'int'


87

แบบฟอร์มไม่ถูกต้อง:

int &z = 12;

แบบฟอร์มที่ถูกต้อง:

int y;
int &r = y;

คำถาม :
ทำไมรหัสแรกผิด? "ความหมาย " ของข้อผิดพลาดในชื่อเรื่องคืออะไร?


5
Temporaries ไม่สามารถผูกไว้กับการอ้างอิงที่ไม่ใช่ค่าคงที่ int (12) เป็นชั่วคราวในกรณีนี้
Prasoon Saurav

@PrasoonSaurav ชั่วคราว 12 หมายความว่าอย่างไร ขาดแนวคิดที่นี่ (ในส่วนของฉัน :))
Aquarius_Girl

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

@KerrekSB ความต้องการที่พบบ่อยที่สุดสำหรับการอ้างอิงที่มีผลผูกพันกับวัตถุ rvalue น่าจะเป็น(ostringstream() << "x=" << x).str()
สงสัยว่า

@curiousguy: ใช่นั่นคือเนื้อหาของลิงค์ที่ฉันโพสต์
Kerrek SB

คำตอบ:


133

C ++ 03 3.10 / 1 พูดว่า: "ทุกนิพจน์เป็น lvalue หรือ rvalue" สิ่งสำคัญคือต้องจำไว้ว่าค่า lvalueness เทียบกับ rvalueness เป็นคุณสมบัติของการแสดงออกไม่ใช่ของวัตถุ

Lvalues ​​ออบเจ็กต์ชื่อที่คงอยู่นอกเหนือจากนิพจน์เดียว ตัวอย่างเช่นobj, *ptr, ptr[index]และ++xเป็น lvalues ทั้งหมด

Rvalues ​​คือชั่วคราวที่ระเหยเมื่อสิ้นสุดนิพจน์เต็มที่พวกมันมีชีวิตอยู่ ("ที่อัฒภาค") ตัวอย่างเช่น1729, x + y, std::string("meow")และx++มี rvalues ทั้งหมด

แอดเดรส - ของโอเปอเรเตอร์กำหนดให้ "ตัวถูกดำเนินการต้องเป็นค่า lvalue" ถ้าเราสามารถใช้แอดเดรสของนิพจน์หนึ่งนิพจน์นั้นคือ lvalue มิฉะนั้นจะเป็น rvalue

 &obj; //  valid
 &12;  //invalid

2
" ถ้าเราสามารถรับแอดเดรสของนิพจน์เดียวนิพจน์จะเป็น lvalue มิฉะนั้นจะเป็น rvalue " ถ้ามีเพียง C ++ เท่านั้นที่ง่าย! (แต่ความแตกต่างเล็กน้อยไม่เกี่ยวข้องจริงๆที่นี่) "Rvalues ​​เป็นชั่วคราว" อะไร วัตถุ?
ซอกแซก

2
@curiousguyRvalues ​​คือชั่วคราวที่หายไปในตอนท้ายของนิพจน์เต็มที่พวกเขาอาศัยอยู่ เพียงแค่ตอบว่าทำไมint & = 12;นิพจน์จึงไม่ถูกต้องมาตรฐานกล่าวว่าลิเทอรัลของสตริงเป็นค่า lvalue ตัวอักษรอื่น ๆ เป็นค่า rvalues
BruceAdi

@curiousguy: ใช่ Rvalues ​​เป็นวัตถุชั่วคราวแต่ไม่ใช่ทุก rvalues ​​เป็นวัตถุชั่วคราว บางอย่างไม่ใช่สิ่งของด้วยซ้ำ
Nawaz

" ตัวอย่างเช่น 1729, x + y, std :: string (" meow ") และ x ++ เป็นค่า rvalues ​​ทั้งหมด " แต่std::string("meow")สร้างออบเจ็กต์ประเภทstd::stringและให้ค่า r ที่กำหนดอ็อบเจ็กต์1729นี้ไม่มีผลข้างเคียงและให้ค่า 1729 เป็น rvalue intของพิมพ์
ซอกแซก

1
@curiousguy: ข้อความ"Lvalues name objects that persist beyond a single expression."นี้เป็นคำสั่งที่ถูกต้อง 100% ในทางกลับกันตัวอย่างของคุณ(const int &)1ไม่ถูกต้องเนื่องจากไม่ใช่วัตถุ "ชื่อ"
Nawaz

53
int &z = 12;

ทางด้านขวามืออ็อบเจ็กต์ชั่วคราวประเภทintถูกสร้างขึ้นจากอินทิกรัลลิเทอรั12ล แต่ชั่วคราวไม่สามารถถูกผูกไว้กับการอ้างอิงที่ไม่ใช่ const ดังนั้นข้อผิดพลาด เหมือนกับ:

int &z = int(12); //still same error

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

วัตถุชั่วคราวสามารถถูกผูกไว้กับการอ้างอิง const ซึ่งหมายความว่าคุณสามารถทำได้:

const int &z = 12; //ok

C ++ 11 และ Rvalue Reference:

เพื่อความสมบูรณ์ฉันต้องการเพิ่มเติมว่า C ++ 11 ได้แนะนำ rvalue-reference ซึ่งสามารถผูกกับวัตถุชั่วคราวได้ ดังนั้นใน C ++ 11 คุณสามารถเขียนสิ่งนี้:

int && z = 12; //C+11 only 

โปรดทราบว่ามี&&intead ของ&. นอกจากนี้โปรดทราบว่าconstไม่จำเป็นอีกต่อไปแม้ว่าออบเจ็กต์ที่zผูกกับเป็นอ็อบเจ็กต์ชั่วคราวที่สร้างขึ้นจากอินทิกรัล - ลิเทอรั12

ตั้งแต่ C ++ 11 ได้แนะนำrvalue อ้างอิง , int&นี้เรียกว่าต่อจากนี้ไปlvalue อ้างอิง


10

12int&เป็นค่าคงที่รวบรวมเวลาที่ไม่สามารถเปลี่ยนแปลงได้แตกต่างจากข้อมูลอ้างอิงจาก สิ่งที่คุณสามารถทำได้คือ

const int& z = 12;

2
@curiousguy สอดคล้องกันคุณเคยพูดว่า "คุณต้องเข้าใจว่านี่คือกฎ C ++ มีอยู่จริงและไม่จำเป็นต้องมีเหตุผลใด ๆ " (ไม่ต้องพูดถึงฉบับพิมพ์ครั้งแรก) ฉันจะตีความคำบ่นของคุณว่าไม่ให้สิ่งที่เป็นไปตามที่ตัวเองต้องการได้อย่างไร?
Michael Krelin - แฮ็กเกอร์

2
@ MichaelKrelin-hacker: ในทางเทคนิคแล้วคุณไม่สามารถ (เคย) เชื่อมโยงการอ้างอิงกับค่า (หรือรวบรวมค่าคงที่ของเวลา) ได้มาตรฐานค่อนข้างชัดเจนว่าเกิดอะไรขึ้นจริง: มิฉะนั้นจะมีการสร้าง "cv1 T1" ชั่วคราวและ เริ่มต้นจากนิพจน์ initializer โดยใช้กฎสำหรับการเริ่มต้นการคัดลอกแบบไม่อ้างอิง (8.5) จากนั้นการอ้างอิงจะถูกผูกไว้กับชั่วคราวนั่นคือไวยากรณ์ได้รับอนุญาต แต่ความหมายไม่ใช่การเชื่อมโยงการอ้างอิงกับค่าคงที่ แต่เป็นการผูกไว้กับชั่วคราวที่สร้างขึ้นโดยนัย
David Rodríguez - น้ำลายไหล

1
@curiousguy: กฎของภาษาเป็นส่วนหนึ่งของการออกแบบและบ่อยกว่านั้นมีเหตุผลว่าทำไมภาษาจึงได้รับการออกแบบเช่นนั้น ในกรณีนี้เช่นเดียวกับใน C คุณไม่ได้รับอนุญาตให้ใช้ที่อยู่ของค่า (ไม่มี) และคุณไม่สามารถผูกข้อมูลอ้างอิงได้ ตอนนี้ให้พิจารณาฟังก์ชันvoid f( vector<int> const & )ซึ่งเป็นสำนวนที่จะส่งผ่านเวกเตอร์ที่ไม่ต้องแก้ไข ปัญหาตอนนี้คือf( vector<int>(5) )จะไม่ถูกต้องและผู้ใช้จะต้องจัดเตรียมโอเวอร์โหลดที่แตกต่างออกvoid f( vector<int> v ) { f(v); }ไป
David Rodríguez - น้ำลายไหล

1
... ตอนนี้เพราะจะทำให้ผู้ใช้ภาษาเจ็บปวดนักออกแบบจึงตัดสินใจว่าคอมไพเลอร์จะดำเนินการที่เทียบเท่ากับคุณในการโทรf( vector<int>(5) )คอมไพลเลอร์จะสร้างชั่วคราวจากนั้นเชื่อมโยงการอ้างอิงกับชั่วคราวนั้นและในทำนองเดียวกัน หากมีการแปลงโดยนัยจาก5โดยตรง สิ่งนี้ช่วยให้คอมไพลเลอร์สร้างลายเซ็นเดียวสำหรับฟังก์ชันและเปิดใช้งานฟังก์ชันผู้ใช้คนเดียว จากนั้นจะมีการกำหนดพฤติกรรมที่คล้ายกันสำหรับการใช้การอ้างอิงคงที่เพื่อความสอดคล้อง
David Rodríguez - น้ำลายไหล

1
@ MichaelKrelin-hacker: การอ้างอิงเป็นนามแฝงของวัตถุและค่าไม่ใช่วัตถุ ขึ้นอยู่กับบริบทการอ้างอิงอาจเป็นเพียงนามแฝงที่คอมไพลเลอร์ลบการอ้างอิงและใช้ตัวระบุเพื่อหมายถึงสิ่งที่วัตถุดั้งเดิมหมายถึง ( T const & r = *ptr;การใช้rฟังก์ชันใด ๆ ในภายหลังสามารถแทนที่ได้*ptrและrไม่จำเป็นต้องมีอยู่ในรันไทม์) หรืออาจต้องใช้งานโดยเก็บที่อยู่ของวัตถุที่เป็นนามแฝง (พิจารณาจัดเก็บข้อมูลอ้างอิงในฐานะสมาชิกของวัตถุ) ซึ่งใช้เป็นตัวชี้การอ้างอิงอัตโนมัติ
David Rodríguez - น้ำลายไหล

4

การเชื่อมโยงการอ้างอิงที่ไม่ใช่ const และ const เป็นไปตามกฎที่แตกต่าง

นี่คือกฎของภาษา C ++:

  • นิพจน์ที่ประกอบด้วยตัวเลขตามตัวอักษร ( 12) คือ "rvalue"
  • ไม่อนุญาตให้สร้างการอ้างอิงที่ไม่ใช่ const ด้วย rvalue: int &ri = 12;เป็นรูปแบบที่ไม่ถูกต้อง
  • ได้รับอนุญาตให้สร้างการอ้างอิง const ด้วย rvalue: ในกรณีนี้อ็อบเจ็กต์ที่ไม่มีชื่อถูกสร้างโดยคอมไพเลอร์ วัตถุนี้จะคงอยู่ตราบเท่าที่มีการอ้างอิงอยู่

คุณต้องเข้าใจว่านี่คือกฎ C ++ พวกเขาเป็นเพียง

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

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


0

การอ้างอิงคือ "ตัวชี้ที่ซ่อนอยู่" (ไม่ใช่ค่าว่าง) สำหรับสิ่งที่เปลี่ยนแปลงได้ (ค่า lvalues) คุณไม่สามารถกำหนดให้เป็นค่าคงที่ มันควรจะเป็น "ตัวแปร"

แก้ไข ::

ฉันกำลังคิดถึง

int &x = y;

เกือบจะเทียบเท่ากับ

int* __px = &y;
#define x (*__px)

__pxชื่อใหม่อยู่ที่ไหนและ#define xผลงานเฉพาะในบล็อกที่มีการประกาศxการอ้างอิง


1
ทำไมคุณถึงทำได้ถ้าข้อมูลอ้างอิงคือconst:)
Michael Krelin - แฮ็กเกอร์

แต่ตัวอย่างของผู้โพสต์ไม่ใช่const
Basile Starynkevitch

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

" อ้างอิง 'ชี้ที่ซ่อนอยู่' " ผิด " กับสิ่งที่สามารถเปลี่ยน " ผิด " สิ่งที่สามารถเปลี่ยน (lvalues). " ผิด
curiousguy

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