ฉันกำลังอ่านเกี่ยวกับลำดับของการละเมิดการประเมินผลและพวกเขายกตัวอย่างที่ทำให้ฉันสงสัย
1) หากผลข้างเคียงของวัตถุสเกลาร์นั้นไม่ได้ถูกจัดลำดับสัมพันธ์กับผลข้างเคียงอื่นในวัตถุสเกลาร์เดียวกันพฤติกรรมนั้นจะไม่ได้กำหนดไว้
// snip f(i = -1, i = -1); // undefined behavior
ในบริบทi
นี้เป็นวัตถุสเกลาร์ซึ่งเห็นได้ชัดว่าหมายถึง
ประเภทเลขคณิต (3.9.1), ประเภทการแจงนับ, ประเภทตัวชี้, ตัวชี้ไปยังประเภทสมาชิก (3.9.2), std :: nullptr_t, และเวอร์ชันที่ผ่านการรับรอง cv ของประเภทเหล่านี้ (3.9.3) เรียกว่าประเภทสเกลาร์
ฉันไม่เห็นว่าคำสั่งนั้นคลุมเครือในกรณีนั้นอย่างไร สำหรับฉันดูเหมือนว่าไม่ว่าอาร์กิวเมนต์แรกหรืออาร์กิวเมนต์ที่สองจะได้รับการประเมินก่อนi
จะสิ้นสุดลง-1
และอาร์กิวเมนต์ทั้งสองก็เช่น-1
กัน
บางคนช่วยอธิบายได้ไหม?
UPDATE
ฉันขอขอบคุณการอภิปรายทั้งหมด ถึงตอนนี้ฉันชอบคำตอบของ @ harmicมากเพราะมันทำให้เกิดข้อผิดพลาดและความซับซ้อนของการกำหนดคำสั่งนี้แม้จะมองไปข้างหน้าอย่างรวดเร็วในทันที @ acheong87ชี้ให้เห็นปัญหาบางอย่างที่เกิดขึ้นเมื่อใช้การอ้างอิง แต่ฉันคิดว่ามันเป็นมุมฉากกับผลข้างเคียงที่ตามมาของคำถามนี้
สรุป
เนื่องจากคำถามนี้ได้รับความสนใจเป็นจำนวนมากฉันจะสรุปประเด็น / คำตอบหลัก ๆ ก่อนอื่นให้ฉันพูดนอกเรื่องเล็ก ๆ เพื่อชี้ให้เห็นว่า "ทำไม" สามารถมีความหมายที่แตกต่างกันอย่างใกล้ชิด แต่มีความหมายที่แตกต่างกันกล่าวคือ "สำหรับสาเหตุอะไร", " ด้วยเหตุผลอะไร" และ "สำหรับวัตถุประสงค์อะไร" ฉันจะจัดกลุ่มคำตอบโดยที่ความหมายเหล่านั้นของ "ทำไม" พวกเขาพูดถึง
สำหรับสาเหตุอะไร
คำตอบหลักมาจากPaul DraperโดยMartin Jให้คำตอบที่คล้ายกัน แต่ไม่ครอบคลุม คำตอบของ Paul Draper ทำให้เดือดร้อน
มันเป็นพฤติกรรมที่ไม่ได้กำหนดเพราะมันไม่ได้กำหนดว่าพฤติกรรมคืออะไร
คำตอบนั้นโดยรวมนั้นดีมากในแง่ของการอธิบายสิ่งที่มาตรฐาน C ++ พูด นอกจากนี้ยังอยู่บางกรณีที่เกี่ยวข้องกับการ UB เช่นและf(++i, ++i);
f(i=1, i=-1);
ในกรณีแรกที่เกี่ยวข้องมันไม่ชัดเจนว่าการโต้แย้งครั้งแรกควรเป็นi+1
และครั้งที่สองi+2
หรือในทางกลับกัน; ในครั้งที่สองมันไม่ชัดเจนว่าi
ควรจะเป็น 1 หรือ -1 หลังจากการเรียกใช้ฟังก์ชัน ทั้งสองกรณีนี้เป็น UB เพราะอยู่ภายใต้กฎต่อไปนี้:
หากผลข้างเคียงของวัตถุสเกลาร์นั้นสัมพันธ์กันกับผลข้างเคียงอื่นในวัตถุสเกลาร์เดียวกันพฤติกรรมนั้นจะไม่ได้กำหนดไว้
ดังนั้นจึงf(i=-1, i=-1)
เป็น UB เพราะมันตกอยู่ภายใต้กฎเดียวกันแม้ว่าความตั้งใจของโปรแกรมเมอร์คือ (IMHO) ชัดเจนและไม่คลุมเครือ
พอลเดรเปอร์ยังทำให้ชัดเจนในบทสรุปของเขาว่า
มันสามารถถูกกำหนดพฤติกรรม? ใช่. มันถูกกำหนดไว้? เลขที่
ซึ่งนำเรามาสู่คำถามที่ว่า "ด้วยเหตุผล / วัตถุประสงค์อะไรที่ถูกf(i=-1, i=-1)
ทิ้งไว้เป็นพฤติกรรมที่ไม่ได้กำหนด"
ด้วยเหตุผลใด / วัตถุประสงค์
แม้ว่าจะมีการกำกับดูแลบางอย่าง (อาจจะประมาท) ในมาตรฐาน C ++ แต่การละเว้นส่วนใหญ่นั้นสมเหตุสมผลและให้บริการตามวัตถุประสงค์เฉพาะ แม้ว่าฉันจะทราบว่าจุดประสงค์มักจะ "ทำให้งานของนักเขียนคอมไพเลอร์ง่ายขึ้น" หรือ "รหัสเร็วขึ้น" แต่ฉันก็สนใจที่จะรู้ว่ามีเหตุผลอะไรที่ดี f(i=-1, i=-1)
ในฐานะ UB
harmicและsupercatให้คำตอบหลักที่ให้เหตุผลสำหรับ UB Harmic ชี้ให้เห็นว่าคอมไพเลอร์ที่ปรับให้เหมาะสมซึ่งอาจแยกการปฏิบัติการปรมาณูออกมาอย่างเห็นได้ชัดเป็นคำสั่งหลายเครื่องและมันอาจจะแทรกคำแนะนำเหล่านั้นเพื่อความเร็วที่เหมาะสม สิ่งนี้อาจนำไปสู่ผลลัพธ์ที่น่าประหลาดใจ: i
จบลงที่ -2 ในสถานการณ์ของเขา! ดังนั้นอันตรายแสดงให้เห็นว่าการกำหนดค่าเดียวกันให้กับตัวแปรมากกว่าหนึ่งครั้งสามารถมีผลร้ายหากการดำเนินการจะตามมา
supercat ให้คำอธิบายเกี่ยวกับข้อผิดพลาดในการพยายามf(i=-1, i=-1)
ทำสิ่งที่ดูเหมือนว่าควรจะทำ เขาชี้ให้เห็นว่าในบางสถาปัตยกรรมมีข้อ จำกัด อย่างหนักต่อการเขียนหลายอย่างพร้อมกันไปยังที่อยู่หน่วยความจำเดียวกัน f(i=-1, i=-1)
เรียบเรียงอาจมีช่วงเวลาที่ยากจับนี้ถ้าเราได้จัดการกับสิ่งที่น้อยกว่าเล็กน้อย
davidfยังให้ตัวอย่างของคำสั่งแบบแทรกสอดคล้ายกับของ harmic
ถึงแม้ว่าจะมีทั้งฮาร์โมนิกตัวอย่างของซูเปอร์แคทและดาวิฟก็ถูกประดิษฐ์ขึ้นมาบ้าง แต่พวกมันก็ยังคงให้เหตุผลที่จับf(i=-1, i=-1)
ต้องได้
ฉันยอมรับคำตอบของ harmic เพราะมันทำงานได้ดีที่สุดในการจัดการกับความหมายทั้งหมดว่าทำไมถึงแม้ว่าคำตอบของ Paul Draper พูดถึงส่วน "สาเหตุที่ทำให้" ดีขึ้น
คำตอบอื่น ๆ
JohnBชี้ให้เห็นว่าหากเราพิจารณาตัวดำเนินการที่ได้รับมอบหมายมากเกินไป (แทนที่จะเป็นสเกลาร์ธรรมดา) เราก็สามารถประสบปัญหาเช่นกัน
f(i-1, i = -1)
หรือสิ่งที่คล้ายกัน
std::nullptr_t
และพันธุ์ที่ผ่านการคัดเลือกรุ่นของประเภทนี้ (3.9.3) จะเรียกว่าประเภทเกลา "