ส่งคืนชนิดของ '?:' (ตัวดำเนินการแบบมีเงื่อนไข)


208

ทำไมคนแรกถึงกลับมาอ้างอิง?

int x = 1;
int y = 2;
(x > y ? x : y) = 100;

ในขณะที่สองไม่ได้?

int x = 1;
long y = 2;
(x > y ? x : y) = 100;

อันที่จริงที่สองไม่ได้รวบรวมเลย - "ไม่ lvalue ซ้ายของการมอบหมาย"


1
อืมก็เหมือนกับการหากรณีพิเศษสำหรับขนมปังอบไม่เคยมาที่นี่ครั้งเดียว
Ulterior


เนื่องจากการกำหนดประเภทให้กับนิพจน์จะบอกเป็นนัยว่ามีอย่างน้อยหนึ่งเทอมเทอมนี้จะไม่เป็นค่า l อีกต่อไป
Yves Daoust

คำตอบ:


173

นิพจน์ไม่มีประเภทผลตอบแทน แต่มีประเภทและเนื่องจากเป็นที่รู้จักในมาตรฐาน C ++ ล่าสุด - หมวดค่า

นิพจน์เงื่อนไขสามารถเป็นlvalueหรือrvalue นี่คือหมวดค่าของมัน (นี่คือการทำให้เข้าใจง่าย, ในC++11เรามี lvalues, xvalues ​​และ prvalues)

ในแง่กว้างและเรียบง่ายlvalueหมายถึงวัตถุในหน่วยความจำและrvalueเป็นเพียงค่าที่อาจไม่จำเป็นต้องแนบกับวัตถุในหน่วยความจำ

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

สำหรับการแสดงออกเงื่อนไข ( ?:) จะเป็นlvalue (อีกครั้งในวงกว้างและแง่ง่าย) ที่สองและสามตัวถูกดำเนินการจะต้องเป็นlvaluesประเภทเดียวกัน นี่เป็นเพราะประเภทและประเภทค่าของนิพจน์เงื่อนไขถูกกำหนดในเวลารวบรวมและต้องเหมาะสมว่าเงื่อนไขเป็นจริงหรือไม่ หากหนึ่งในตัวถูกดำเนินการจะต้องมีการแปลงเป็นชนิดที่แตกต่างกันเพื่อให้ตรงกับอื่น ๆ แล้วการแสดงออกเงื่อนไขไม่สามารถเป็นlvalueเป็นผลมาจากการแปลงนี้จะไม่เป็นlvalue

ข้อมูลอ้างอิง ISO / IEC 14882: 2011:

3.10 [basic.lval] ค่าและค่า (เกี่ยวกับหมวดค่า)

5.15 [expr.cond] โอเปอเรเตอร์แบบมีเงื่อนไข (กฎสำหรับประเภทและค่าประเภทที่นิพจน์เงื่อนไขมี)

5.17 [expr.ass] การกำหนดและตัวดำเนินการมอบหมายผสม (ข้อกำหนดว่า lhs ของการมอบหมายต้องเป็นค่า lvalue ที่สามารถแก้ไขได้)


3
และเมื่ออ่านค่า xvalue และ prvalue (เนื่องจากฉันไม่เคยได้ยินพวกเขามาก่อนคำตอบของคุณ) ฉันได้พบกับโพสต์ SO ที่มีประโยชน์นี้: stackoverflow.com/questions/3601602/…
ปุย

an rvalue is just a value that may not necessarily be *attached* to an object in memory.คุณช่วยอธิบายสิ่งนี้ในระยะที่ง่ายขึ้นได้ไหม? . คุณหมายถึงtype and value *category*อะไร. ขอบคุณ
Mr.Anubis

@SoulReaper: prvalue, xvalue, glvalueเป็นหมวดหมู่ค่า
Xeo

@ Xeo ฉันขอขอบคุณความช่วยเหลือ แต่คุณสามารถบอกได้ว่าเขาหมายถึงอะไรโดยrvalue เป็นเพียงค่าที่อาจไม่จำเป็นต้องแนบกับวัตถุในหน่วยความจำ ? ด้วยตัวอย่าง
Mr.Anubis

@SoulReaper: ผมคิดว่าที่เขาพูดเกี่ยวกับสิ่งที่ชอบtrue, this, enumค่า สิ่งเหล่านั้นมีค่ามากมาย ("บริสุทธิ์" rvalues) แต่ไม่ได้อยู่ในความทรงจำ
Xeo

57

ชนิดของ?:นิพจน์ประกอบไปด้วยเป็นประเภททั่วไปของอาร์กิวเมนต์ที่สองและสาม หากทั้งสองประเภทเหมือนกันคุณจะได้รับการอ้างอิงกลับ หากพวกเขาสามารถแลกเปลี่ยนกันได้หนึ่งคนได้รับเลือกและคนอื่น ๆ ได้รับการแปลง (เลื่อนตำแหน่งในกรณีนี้) เนื่องจากคุณไม่สามารถส่งคืนการอ้างอิง lvalue ไปยังชั่วคราว (ตัวแปรที่ถูกแปลง / เลื่อนระดับ), ประเภทของมันจึงเป็นประเภทค่า


แต่ y มากกว่า x ดังนั้นจึงไม่จำเป็นต้องมีการส่งเสริมสำหรับกรณีนี้มันสามารถส่งคืนการอ้างอิงถึง y อืม ... แต่ฉันเห็นด้วยมันคงจะแปลก
Yola

1
@ Mr.TAMER: ฉันต้องการขุดผ่านมาตรฐาน : <
Xeo

3
@Yola: เนื่องจากประเภทเป็นแนวคิดรวบยอดเวลาใน C ++ ค่าส่งคืนจริงของนิพจน์จึงไม่สำคัญ
Xeo

1
คุณไม่ได้รับการอ้างอิงกลับคุณจะได้รับ lvalue
Suma

1
@ Xeo: ไม่ได้อยู่ในคำศัพท์ C ++ แต่;)
เซบาสเตียนมัค

19

มันไม่สามารถส่งคืนค่าlvalue ได้เนื่องจากจะต้องส่งเสริมประเภทของxการจับคู่กับชนิดของimplicitly y(เนื่องจากทั้งสองด้าน:ไม่ใช่ประเภทเดียวกัน) และต้องสร้างชั่วคราว


มาตรฐานพูดอะไร ( n1905 )

นิพจน์ 5.17 การกำหนดและตัวดำเนินการกำหนดผสม

5.17 / 3

หากตัวถูกดำเนินการตัวที่สองและที่สามมีประเภทที่แตกต่างกันและมีประเภทคลาส (อาจเป็น cv ที่ผ่านการรับรอง) จะมีความพยายามในการแปลงตัวถูกดำเนินการแต่ละตัวเป็นประเภทอื่น กระบวนการในการพิจารณาว่านิพจน์ตัวถูกดำเนินการ E1 ประเภท T1 สามารถแปลงให้ตรงกับนิพจน์ตัวถูกดำเนินการ E2 ประเภท T2 ถูกกำหนดดังนี้:

- หาก E2 เป็น lvalue: E1 สามารถแปลงให้ตรงกับ E2 หาก E1 สามารถแปลงได้โดยปริยาย (ข้อ 4) เป็นประเภท“ การอ้างอิงถึง T2” โดยขึ้นอยู่กับข้อ จำกัด ที่ในการแปลงการอ้างอิงต้องผูกโดยตรง (8.5.3) ) ถึง E1

- หาก E2 เป็นค่า rvalue หรือหากการแปลงข้างต้นไม่สามารถทำได้:

- ถ้า E1 และ E2 มีประเภทชั้นเรียนและประเภทชั้นพื้นฐานเหมือนกันหรือหนึ่งเป็นชั้นฐานของอื่น ๆ : E1 สามารถแปลงให้ตรงกับ E2 ถ้าชั้นของ T2 เป็นประเภทเดียวกันเป็นหรือชั้นฐานของ คลาสของ T1 และคุณสมบัติ cv ของ T2 เป็นคุณสมบัติของ cv เดียวกันกับที่หรือคุณสมบัติของ cv-qualification ที่มากกว่าของ cv-qualification ของ T1 หากมีการใช้การแปลง E1 จะเปลี่ยนเป็น rvalue ประเภท T2 ที่ยังคงอ้างอิงถึงวัตถุคลาสต้นฉบับ (หรือ subobject ที่เหมาะสมของมัน) [ หมายเหตุ: นั่นคือไม่มีการคัดลอก - หมายเหตุท้าย ] โดยการคัดลอกเริ่มต้นชั่วคราวของประเภท T2 จาก E1 และใช้ชั่วคราวนั้นเป็นตัวถูกดำเนินการแปลง

มิฉะนั้น (เช่นถ้าE1หรือ E2 มีประเภทคลาสที่ไม่ใช่หรือถ้าพวกเขาทั้งสองมีประเภทชั้นเรียน แต่ชั้นเรียนพื้นฐานไม่เหมือนกันหรือเป็นหนึ่งในชั้นฐานของอื่น ๆ ): E1 สามารถแปลงให้ตรงกับ E2 ถ้า E1 สามารถ แปลงเป็นประเภทที่นิพจน์ E2 มีโดยปริยายหาก E2 ถูกแปลงเป็น rvalue (หรือเป็นประเภทที่มีหาก E2 เป็น rvalue)

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


5.17 / 4

หากตัวถูกดำเนินการตัวที่สองและสามเป็น lvalues ​​และมีชนิดเดียวกันผลลัพธ์จะเป็นชนิดนั้นและเป็น lvalue และเป็นบิตฟิลด์ถ้าตัวถูกดำเนินการตัวที่สองหรือสามเป็นบิตฟิลด์หรือทั้งสองเป็นบิต สาขา


5.17 / 5

มิฉะนั้นผลลัพธ์จะเป็นค่า rvalue หากตัวถูกดำเนินการตัวที่สองและสามไม่มีประเภทเดียวกันและมีประเภทคลาส (อาจเป็น cv ที่ผ่านการรับรอง) การแก้ปัญหาการโอเวอร์โหลดจะใช้เพื่อกำหนดการแปลง (ถ้ามี) ที่จะใช้กับตัวถูกดำเนินการ (13.3.1.2, 13.6) . หากการแก้ปัญหาโอเวอร์โหลดล้มเหลวโปรแกรมจะเกิดรูปแบบไม่ถูกต้อง มิฉะนั้นการแปลงที่กำหนดไว้จะถูกนำไปใช้และตัวถูกดำเนินการแปลงถูกนำมาใช้แทนตัวถูกดำเนินการเดิมสำหรับส่วนที่เหลือของส่วนนี้

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