Jika saya meneruskan kode berikut melalui snapshot GCC 4.7 saya, ia mencoba menyalin unique_ptr
s ke dalam vektor.
#include <vector>
#include <memory>
int main() {
using move_only = std::unique_ptr<int>;
std::vector<move_only> v { move_only(), move_only(), move_only() };
}
Jelas itu tidak dapat berfungsi karena std::unique_ptr
tidak dapat disalin:
kesalahan: penggunaan fungsi yang dihapus 'std :: unique_ptr <_Tp, _Dp> :: unique_ptr (const std :: unique_ptr <_Tp, _Dp> &) [dengan _Tp = int; _Dp = std :: default_delete; std :: unique_ptr <_Tp, _Dp> = std :: unique_ptr] '
Apakah GCC benar dalam mencoba menyalin petunjuk dari daftar penginisialisasi?
c++
c++11
initializer-list
move-semantics
R. Martinho Fernandes
sumber
sumber
Jawaban:
Sinopsis
<initializer_list>
dalam 18.9 memperjelas bahwa elemen dari daftar penginisialisasi selalu diteruskan melalui referensi-konst. Sayangnya, tampaknya tidak ada cara untuk menggunakan semantik bergerak dalam elemen daftar penginisialisasi dalam revisi bahasa saat ini.Secara khusus, kami memiliki:
sumber
const
, yang tidak dapat dibuang dalam program yang dibentuk dengan baik.Sunting: Karena @Johannes sepertinya tidak ingin memposting solusi terbaik sebagai jawaban, saya akan melakukannya.
Iterator yang dikembalikan oleh
std::make_move_iterator
akan memindahkan elemen menunjuk ke saat didereferensi.Jawaban asli: Kami akan menggunakan tipe pembantu kecil di sini:
Sayangnya, kode langsung di sini tidak akan berfungsi:
Karena standar, untuk alasan apa pun, tidak mendefinisikan konstruktor salinan konversi seperti ini:
Yang
initializer_list<rref_wrapper<move_only>>
dibuat oleh brace-init-list ({...}
) tidak akan mengonversi menjadiinitializer_list<move_only>
yangvector<move_only>
diambil. Jadi kita membutuhkan inisialisasi dua langkah di sini:sumber
std::ref
, bukan? Mungkin harus dipanggilstd::rref
.move_only m[] = { move_only(), move_only(), move_only() }; std::vector<move_only> v(std::make_move_iterator(m), std::make_move_iterator(m + 3));
.move_iterator
.std::distance
untuk meneruskan-atau-lebih baik iterator danstd::move_iterator
menyesuaikan kategori iterator yang mendasarinya. Bagaimanapun, solusi yang bagus dan ringkas. Posting itu sebagai jawaban, mungkin?Seperti yang disebutkan dalam jawaban lain, perilaku dari
std::initializer_list
adalah memegang benda berdasarkan nilai dan tidak membiarkan bergerak keluar, jadi ini tidak mungkin. Berikut ini satu solusi yang mungkin, menggunakan pemanggilan fungsi di mana penginisialisasi diberikan sebagai argumen variadic:Sayangnya
multi_emplace(foos, {});
gagal karena tidak dapat menyimpulkan tipe untuk{}
, jadi untuk objek yang akan dibangun secara default Anda harus mengulangi nama kelas. (atau gunakanvector::resize
)sumber
Menggunakan trik Johannes Schaub
std::make_move_iterator()
denganstd::experimental::make_array()
, Anda dapat menggunakan fungsi helper:Lihat langsung Coliru.
Mungkin seseorang dapat memanfaatkan
std::make_array()
tipu daya untuk memungkinkanmake_vector()
melakukan sesuatu secara langsung, tetapi saya tidak melihat bagaimana (lebih tepatnya, saya mencoba apa yang saya pikir harus berhasil, gagal, dan pindah). Dalam kasus apa pun, compiler harus dapat melakukan transformasi array ke vektor, seperti yang dilakukan Clang dengan O2 aktif GodBolt.sumber
Seperti yang telah ditunjukkan, tidak mungkin untuk menginisialisasi vektor tipe hanya bergerak dengan daftar penginisialisasi. Solusi yang awalnya diusulkan oleh @Johannes berfungsi dengan baik, tetapi saya punya ide lain ... Bagaimana jika kita tidak membuat array sementara dan kemudian memindahkan elemen dari sana ke vektor, tetapi menggunakan penempatan
new
untuk menginisialisasi array ini yang sudah menggantikan blok memori vektor?Inilah fungsi saya untuk menginisialisasi vektor
unique_ptr
menggunakan paket argumen:sumber
result.data()
bukan penunjuk ke beberapa memori acak. Ini adalah penunjuk ke suatu objek . Pikirkan apa yang terjadi pada objek malang itu ketika Anda meletakkan yang baru di atasnya.