Apakah std :: operator unordered_map [] melakukan inisialisasi nol untuk kunci yang tidak ada?

25

Menurut cppreference.com, std::map::operator[]untuk nilai yang tidak ada tidak melakukan inisialisasi nol.

Namun, situs yang sama tidak menyebutkan inisialisasi nol untuk std::unordered_map::operator[], kecuali ia memang memiliki contoh yang bergantung pada ini.

Tentu saja ini hanya situs referensi, bukan standar. Jadi, apakah kode di bawah ini ok atau tidak?

#include <unordered_map>
int main() {
    std::unordered_map<int, int> map;
    return map[42];     // is this guaranteed to return 0?
}
Hyde
sumber
13
@ Ælex Anda tidak dapat dengan andal menguji apakah sesuatu diinisialisasi
idclev 463035818
2
@ Ælex Saya tidak begitu mengerti, bagaimana Anda bisa melakukan non-inisialisasi std::optional?
idclev 463035818
2
@ Ælex tidak ada cara untuk menguji apakah suatu objek diinisialisasi atau tidak karena operasi apa pun pada objek yang tidak diinisialisasi selain inisialisasi menghasilkan Perilaku Tidak Terdefinisi. Sebuah std::optionalobjek yang memegang nilai tidak ada yang terkandung masih merupakan objek diinisialisasi.
bolov
2
Objek nilai diinisialisasi-nilai, bukan diinisialisasi-nol. Untuk tipe skalar ini adalah sama, tetapi untuk tipe kelas mereka berbeda.
aschepler
@bolov Saya mencoba mengujinya kemarin menggunakan gnu 17 dan std 17, dan anehnya yang saya dapatkan adalah nol inisialisasi. Saya pikir std::optional has_valueakan mengujinya tetapi gagal, jadi saya kira Anda benar.
Ælex

Jawaban:

13

Bergantung pada kelebihan apa yang kita bicarakan, std::unordered_map::operator[]setara dengan [unord.map.elem]

T& operator[](const key_type& k)
{
    return try_­emplace(k).first->second;
}

(kelebihan mengambil referensi-nilai hanya bergerak kke try_emplacedan sebaliknya identik)

Jika elemen ada di bawah kunci kdi peta, lalu try_emplacemengembalikan iterator ke elemen itu dan false. Jika tidak, try_emplacemasukkan elemen baru di bawah kunci k, dan kembalikan iterator ke sana dan true [unord.map.modifiers] :

template <class... Args>
pair<iterator, bool> try_emplace(const key_type& k, Args&&... args);

Yang menarik bagi kami adalah belum ada elemen [unord.map.modifiers] / 6 :

Kalau tidak, menyisipkan objek tipe value_­typedibangun denganpiecewise_­construct, forward_­as_­tuple(k), forward_­as_­tuple(std​::​forward<Args>(args)...)

(Kelebihan mengambil referensi-nilai hanya bergerak kke forward_­as_­tupledan, sekali lagi, identik)

Sejak value_typeadalah pair<const Key, T> [unord.map.overview] / 2 , ini memberitahu kita bahwa unsur peta baru akan dibangun sebagai:

pair<const Key, T>(piecewise_­construct, forward_­as_­tuple(k), forward_­as_­tuple(std​::​forward<Args>(args)...));

Karena argskosong ketika datang dari operator[], ini bermuara pada nilai baru kami dibangun sebagai anggota pairdari tidak ada argumen [pairs.pair] / 14 yang merupakan inisialisasi langsung [class.base.init] / 7 dari nilai tipe Tmenggunakan ()sebagai inisialisasi yang bermuara pada nilai inisialisasi [dcl.init] /17.4 . Inisialisasi nilai suatu intadalah inisialisasi nol [dcl.init] / 8 . Dan nol inisialisasi dari sebuah intsecara alami menginisialisasi intke 0 [dcl.init] / 6 .

Jadi ya, kode Anda dijamin untuk mengembalikan 0 ...

Michael Kenzel
sumber
21

Di situs yang Anda tautkan tertulis:

Ketika pengalokasi default digunakan, ini menghasilkan kunci yang dibuat dari kunci dan nilai yang dipetakan diinisialisasi nilai.

Jadi intadalah nilai-diinisialisasi :

Efek inisialisasi nilai adalah:

[...]

4) jika tidak, objek diinisialisasi nol

Inilah sebabnya hasilnya 0.

Api
sumber