vector<int> v;
v.push_back(1);
v.push_back(v[0]);
Jika push_back kedua menyebabkan realokasi, referensi ke integer pertama dalam vektor tidak akan lagi valid. Jadi ini tidak aman?
vector<int> v;
v.push_back(1);
v.reserve(v.size() + 1);
v.push_back(v[0]);
Ini membuatnya aman?
push_back
. Poster lain mencatat ada bug di dalamnya , sehingga tidak menangani case yang Anda jelaskan dengan benar. Tidak ada orang lain, sejauh yang saya tahu, berpendapat bahwa ini bukan bug. Bukan mengatakan itu bukti konklusif, hanya sebuah pengamatan.Jawaban:
Sepertinya http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#526 mengatasi masalah ini (atau sesuatu yang sangat mirip dengannya) sebagai kemungkinan cacat pada standar:
Resolusi yang diusulkan adalah bahwa ini bukan cacat:
sumber
v.insert(v.begin(), v[2]);
tidak dapat memicu realokasi. Jadi bagaimana ini menjawab pertanyaan?Ya, itu aman, dan implementasi perpustakaan standar melompat melalui lingkaran untuk membuatnya begitu.
Saya percaya pelaksana melacak persyaratan ini kembali ke 23.2 / 11 entah bagaimana, tapi saya tidak tahu caranya, dan saya juga tidak bisa menemukan sesuatu yang lebih konkret. Yang terbaik yang bisa saya temukan adalah artikel ini:
http://www.drdobbs.com/cpp/copying-container-elements-from-the-c-li/240155771
Pemeriksaan implementasi libc ++ dan libstdc ++ menunjukkan bahwa mereka juga aman.
sumber
vec.insert(vec.end(), vec.begin(), vec.end());
?vector.push_back
JANGAN menentukan sebaliknya. "Menyebabkan realokasi jika ukuran baru lebih besar dari kapasitas yang lama." dan (atreserve
) "Realokasi membatalkan semua referensi, petunjuk, dan iterator yang mengacu pada elemen dalam urutan."Standar ini menjamin bahkan contoh pertama Anda menjadi aman. Mengutip C ++ 11
[sequence.reqmts]
Jadi, meskipun itu tidak sepele, implementasi harus menjamin itu tidak akan membatalkan referensi ketika melakukan
push_back
.sumber
t
, satu-satunya pertanyaan adalah apakah sebelum atau setelah membuat salinan. Kalimat terakhir Anda tentu salah.t
memenuhi prasyarat yang tercantum, perilaku yang dijelaskan dijamin. Suatu implementasi tidak diizinkan untuk membatalkan prasyarat dan kemudian menggunakannya sebagai alasan untuk tidak berperilaku seperti yang ditentukan.for_each
yang diperlukan untuk tidak membatalkan iterators. Saya tidak dapat menemukan referensi untukfor_each
, tetapi saya melihat pada beberapa algoritma teks seperti "op dan binary_op tidak akan membatalkan iterators atau subranges".Tidak jelas bahwa contoh pertama aman, karena implementasi paling sederhana
push_back
adalah dengan terlebih dahulu realokasi vektor, jika perlu, dan kemudian menyalin referensi.Tetapi setidaknya tampaknya aman dengan Visual Studio 2010. Implementasinya
push_back
melakukan penanganan khusus kasus ketika Anda menekan kembali suatu elemen dalam vektor. Kode ini disusun sebagai berikut:sumber
Ini bukan jaminan dari standar, tetapi sebagai titik data lain,
v.push_back(v[0])
aman untuk libc ++ LLVM .libc ++
std::vector::push_back
panggilan__push_back_slow_path
ketika perlu merealokasi memori:sumber
__swap_out_circular_buffer
, dalam hal ini implementasi ini memang aman.__swap_out_circular_buffer
. (Saya telah menambahkan beberapa komentar untuk diperhatikan.)Versi pertama jelas TIDAK aman:
dari bagian 17.6.5.9
Perhatikan bahwa ini adalah bagian tentang perlombaan data, yang biasanya dipikirkan orang bersamaan dengan threading ... tetapi definisi sebenarnya melibatkan hubungan "terjadi sebelum", dan saya tidak melihat adanya hubungan pemesanan antara beberapa efek samping
push_back
di bermain di sini, yaitu pembatalan referensi tampaknya tidak didefinisikan sebagai diperintahkan sehubungan dengan menyalin-membangun elemen ekor baru.sumber
v[0]
bukan iterator, jugapush_back()
tidak mengambil iterator. Jadi, dari sudut pandang pengacara bahasa, argumen Anda tidak berlaku. Maaf. Saya tahu, bahwa sebagian besar iterator adalah pointer, dan titik pembatalan iterator, hampir sama dengan referensi, tetapi bagian dari standar yang Anda kutip, tidak relevan dengan situasi yang dihadapi.x.push_back(x[0])
itu AMAN.Benar-benar aman.
Dalam contoh kedua yang Anda miliki
yang tidak diperlukan karena jika vektor keluar dari ukurannya, itu akan berarti
reserve
.Vektor bertanggung jawab atas hal ini, bukan Anda.
sumber
Keduanya aman karena push_back akan menyalin nilainya, bukan referensi. Jika Anda menyimpan pointer, itu masih aman sejauh menyangkut vektor, tetapi ketahuilah bahwa Anda akan memiliki dua elemen vektor yang menunjuk ke data yang sama.
Implementasi push_back karena itu harus memastikan bahwa salinan
v[0]
dimasukkan. Dengan contoh balasan, dengan asumsi implementasi yang akan dialokasikan kembali sebelum menyalin, itu tidak akan dengan pasti menambahkan salinanv[0]
dan dengan demikian melanggar spesifikasi.sumber
push_back
akan tetapi juga mengubah ukuran vektor, dan dalam implementasi yang naif ini akan membatalkan referensi sebelum menyalin terjadi. Jadi kecuali Anda dapat mendukungnya dengan kutipan dari standar, saya akan menganggapnya salah.push_back
akan menyalin nilai ke dalam vektor; tetapi (sejauh yang saya bisa lihat) yang mungkin terjadi setelah realokasi, pada titik mana referensi yang dicoba disalin tidak lagi valid.push_back
menerima argumennya dengan referensi .Dari 23.3.6.5/1:
Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid.
Karena kami menyisipkan di akhir, tidak ada referensi yang akan dibatalkan jika vektor tidak diubah ukurannya. Jadi jika vektor
capacity() > size()
maka dijamin akan berfungsi, jika tidak dijamin tidak akan terdefinisi.sumber
references
bagian dari kutipan.push_back
).