Saya membaca tentang urutan pelanggaran evaluasi , dan mereka memberikan contoh yang membingungkan saya.
1) Jika efek samping pada objek skalar tidak berurutan relatif terhadap efek samping lain pada objek skalar yang sama, perilaku tidak terdefinisi.
// snip f(i = -1, i = -1); // undefined behavior
Dalam konteks ini, i
adalah objek skalar , yang tampaknya berarti
Tipe aritmatika (3.9.1), tipe enumerasi, tipe pointer, pointer ke tipe anggota (3.9.2), std :: nullptr_t, dan versi cv-kualifikasi dari tipe-tipe ini (3.9.3) secara kolektif disebut tipe skalar.
Saya tidak melihat bagaimana pernyataan itu ambigu dalam kasus itu. Tampak bagi saya bahwa terlepas dari apakah argumen pertama atau kedua dievaluasi terlebih dahulu, i
berakhir sebagai -1
, dan kedua argumen juga -1
.
Bisakah seseorang menjelaskan?
MEMPERBARUI
Saya sangat menghargai semua diskusi. Sejauh ini, saya sangat suka jawaban @ harmic karena ini mengungkap perangkap dan seluk-beluk mendefinisikan pernyataan ini terlepas dari bagaimana kelihatannya lurus ke depan pada pandangan pertama. @ acheong87 menunjukkan beberapa masalah yang muncul ketika menggunakan referensi, tapi saya pikir itu ortogonal dengan aspek efek samping yang tidak diketahui dari pertanyaan ini.
RINGKASAN
Karena pertanyaan ini mendapat banyak perhatian, saya akan merangkum poin / jawaban utama. Pertama, izinkan saya penyimpangan kecil untuk menunjukkan bahwa "mengapa" dapat telah terkait erat namun halus arti yang berbeda, yaitu "untuk apa penyebabnya ", "untuk apa alasan ", dan "untuk apa tujuan ". Saya akan mengelompokkan jawaban dengan mana dari makna "mengapa" yang mereka bahas.
untuk alasan apa
Jawaban utama di sini berasal dari Paul Draper , dengan Martin J menyumbangkan jawaban yang sama tetapi tidak luas. Jawaban Paul Draper sampai pada
Itu adalah perilaku yang tidak terdefinisi karena tidak didefinisikan apa perilaku itu.
Jawabannya secara keseluruhan sangat baik dalam menjelaskan apa yang dikatakan standar C ++. Ini juga membahas beberapa kasus terkait UB seperti f(++i, ++i);
dan f(i=1, i=-1);
. Dalam kasus pertama terkait, tidak jelas apakah argumen pertama harus i+1
dan yang kedua i+2
atau sebaliknya; di yang kedua, tidak jelas apakah i
harus 1 atau -1 setelah pemanggilan fungsi. Kedua kasus ini adalah UB karena berada di bawah aturan berikut:
Jika efek samping pada objek skalar tidak diikuti relatif terhadap efek samping lain pada objek skalar yang sama, perilaku tidak terdefinisi.
Oleh karena itu, f(i=-1, i=-1)
juga UB karena berada di bawah aturan yang sama, meskipun niat programmer (IMHO) jelas dan tidak ambigu.
Paul Draper juga membuatnya secara eksplisit dalam kesimpulannya itu
Mungkinkah perilaku itu didefinisikan? Iya. Apakah itu didefinisikan? Tidak.
yang membawa kita pada pertanyaan "untuk alasan / tujuan apa yang f(i=-1, i=-1)
tersisa sebagai perilaku yang tidak terdefinisi?"
untuk alasan / tujuan apa
Meskipun ada beberapa kekeliruan (mungkin ceroboh) dalam standar C ++, banyak kelalaian yang beralasan dan melayani tujuan tertentu. Walaupun saya sadar bahwa tujuannya sering kali adalah "membuat pekerjaan kompiler-penulis lebih mudah", atau "kode lebih cepat", saya terutama tertarik untuk mengetahui apakah ada alasan yang baik untuk meninggalkan f(i=-1, i=-1)
UB.
harmic dan supercat memberikan jawaban utama yang memberikan alasan bagi UB. Harmic menunjukkan bahwa kompiler pengoptimal yang mungkin memecah operasi penugasan atom ke dalam beberapa instruksi mesin, dan bahwa itu mungkin lebih lanjut interleave instruksi tersebut untuk kecepatan optimal. Ini dapat menyebabkan beberapa hasil yang sangat mengejutkan: i
berakhir sebagai -2 dalam skenarionya! Dengan demikian, harmic menunjukkan bagaimana menetapkan nilai yang sama ke variabel lebih dari satu kali dapat memiliki efek buruk jika operasi tidak dilanjutkan.
supercat memberikan paparan terkait tentang perangkap mencoba f(i=-1, i=-1)
untuk melakukan apa yang seharusnya dilakukan. Dia menunjukkan bahwa pada beberapa arsitektur, ada batasan keras terhadap beberapa penulisan simultan ke alamat memori yang sama. Kompiler mungkin kesulitan menangkap ini jika kita berurusan dengan sesuatu yang kurang sepele daripada f(i=-1, i=-1)
.
davidf juga memberikan contoh instruksi interleaving yang sangat mirip dengan yang berbahaya.
Meskipun masing-masing contoh berbahaya, supercat dan davidf agak dibuat-buat, secara bersama-sama mereka masih berfungsi untuk memberikan alasan nyata mengapa f(i=-1, i=-1)
harus perilaku yang tidak terdefinisi.
Saya menerima jawaban yang merugikan karena itu melakukan pekerjaan terbaik untuk mengatasi semua makna mengapa, meskipun jawaban Paul Draper membahas bagian "untuk alasan apa" dengan lebih baik.
jawaban lain
JohnB menunjukkan bahwa jika kita mempertimbangkan operator penugasan yang kelebihan beban (bukan hanya skalar biasa), maka kita dapat mengalami masalah juga.
f(i-1, i = -1)
atau sesuatu yang serupa.
std::nullptr_t
,, dan versi yang memenuhi syarat cv dari tipe-tipe ini (3.9.3) secara kolektif disebut tipe skalar . "