Saya baru saja menemukan bug aneh dalam kode saya (C++ 14) yang disebabkan oleh perilaku std::map yang tidak terduga (setidaknya untuk saya). Berikut adalah contoh sederhana yang menunjukkan perilaku:

#include <iostream>
#include <map>

int main()
{
    std::map<int, int> m;
    for(int i = 0; i < 3; ++i) {
        m[m.size()] = m.size();
    }
    for(const std::pair<int, int>& e : m) {
        std::cout << e.first << " " << e.second << std::endl;
    }
    
    return 0;
}

Ini mencetak:

0 1                                                                                                                                                                            
1 2                                                                                                                                                                            
2 3

Saya mengharapkan:

0 0
1 1
2 2

Apa yang terjadi disini? Peta pertama-tama menambahkan elemen baru dengan set first dan baru kemudian (ketika ukuran peta telah meningkat) set second? Saya tidak begitu mengerti mengapa ini masuk akal. Atau ada penjelasan lain? Terima kasih!

3
Petri Hirvonen 23 April 2021, 19:34

2 jawaban

Jawaban Terbaik

Ada beberapa hal yang terjadi dalam ekspresi

m[m.size()] = m.size();

Pertama, m[m.size()] dan = m.size() perlu dievaluasi. Dalam C++14, urutan evaluasi itu diurutkan secara tak tentu. m[m.size()] mungkin terjadi lebih dulu, atau mungkin terjadi kedua. Jika itu terjadi lebih dulu, maka Anda melihat hasil yang Anda terima. Jika itu terjadi kedua, maka Anda mendapatkan output yang Anda harapkan.

Jika kamu mau

0 0
1 1
2 2

Maka Anda perlu menjamin bahwa memesan sendiri. Anda dapat menggunakan map::insert() untuk melakukan hal itu:

m.insert(m.size(), m.size());

Mulai dari C++17, hal ini tidak lagi terjadi. Standar diubah menjadi:

Operator penugasan (=) dan operator penugasan majemuk semuanya mengelompok dari kanan ke kiri. Semua memerlukan nilai yang dapat dimodifikasi sebagai operan kirinya; hasilnya adalah nilai yang mengacu pada operan kiri. Hasil dalam semua kasus adalah bidang-bit jika operan kiri adalah bidang-bit. Dalam semua kasus, penugasan diurutkan setelah perhitungan nilai dari operan kanan dan kiri, dan sebelum perhitungan nilai dari ekspresi penugasan. Operan kanan diurutkan sebelum operan kiri. Sehubungan dengan pemanggilan fungsi yang berurutan tak tentu, operasi penugasan gabungan adalah evaluasi tunggal.

Sekarang menjamin bahwa = m.size() terjadi sebelum m[m.size()], dan Anda mendapatkan pesanan yang Anda harapkan.

8
NathanOliver 23 April 2021, 17:57

Mari kita tuliskan kode loop dengan cara yang lebih rumit:

for(int i = 0; i < 3; ++i) {
    int& temp = m.operator[](m.size());
    temp = m.size();
}

Perhatikan bahwa memanggil operator[] sudah melakukan penyisipan elemen inisialisasi default sebelum Anda menetapkan nilai untuk itu. Oleh karena itu, peta telah berkembang pada saat sisi kanan penugasan dievaluasi.

Untuk memperbaiki masalah ini, pastikan ukurannya ditentukan sebelum Anda menggunakan operator[]:

for(int i = 0; i < 3; ++i) {
    size_t v = m.size();
    m[v] = v;
}
4
Remy Lebeau 23 April 2021, 17:10