Saya memiliki std::vector
objek dari kelas tertentu A
. Kelas tidak sepele dan memiliki konstruktor salinan dan konstruktor bergerak yang ditentukan.
std::vector<A> myvec;
Jika saya mengisi vektor dengan A
objek (menggunakan contoh myvec.push_back(a)
), ukuran vektor akan bertambah, menggunakan konstruktor salinan A( const A&)
untuk membuat salinan baru dari elemen dalam vektor.
Dapatkah saya entah bagaimana memaksakan bahwa konstruktor pemindahan kelas A
memohon digunakan sebagai gantinya?
Jawaban:
Anda perlu memberi tahu C ++ (secara khusus
std::vector
) bahwa konstruktor dan destruktor pemindahan Anda tidak melempar, menggunakannoexcept
. Kemudian konstruktor bergerak akan dipanggil ketika vektor tumbuh.Ini adalah cara mendeklarasikan dan mengimplementasikan konstruktor bergerak yang diikuti oleh
std::vector
:A(A && rhs) noexcept { std::cout << "i am the move constr" <<std::endl; ... some code doing the move ... m_value=std::move(rhs.m_value) ; // etc... }
Jika konstruktor tidak
noexcept
, tidakstd::vector
dapat menggunakannya, karena itu tidak dapat memastikan jaminan pengecualian yang diminta oleh standar.Untuk mengetahui lebih lanjut tentang apa yang diucapkan dalam standar, baca C ++ Move semantik dan Pengecualian
Penghargaan untuk Bo yang mengisyaratkan bahwa itu mungkin ada hubungannya dengan pengecualian. Pertimbangkan juga saran Kerrek SB dan gunakan
emplace_back
jika memungkinkan. Ini bisa lebih cepat (tetapi seringkali tidak), bisa lebih jelas dan lebih kompak, tetapi ada juga beberapa kendala (terutama dengan konstruktor non-eksplisit).Edit , sering kali defaultnya adalah yang Anda inginkan: pindahkan semua yang dapat dipindahkan, salin sisanya. Untuk secara eksplisit memintanya, tulis
A(A && rhs) = default;
Melakukan itu, Anda akan mendapatkan noexcept jika memungkinkan: Apakah default Move constructor didefinisikan sebagai noexcept?
Perhatikan bahwa versi awal Visual Studio 2015 dan yang lebih lama tidak mendukungnya, meskipun mendukung semantik bergerak.
sumber
value_type
's bergerak ctor adalahnoexcept
? Mungkin bahasa membatasi set kandidat panggilan fungsi ketika cakupan panggilan juga merupakannoexcept
fungsi?noexcept
konstruktor pemindahan.is_nothrow_move_constructible
akan benar jika adanothrow
konstruktor salinan. Saya tidak mengetahui kasus nyata dari pembuatnothrow
salinan mahal jadi tidak jelas apakah itu benar-benar penting.noexcept
di header dan implementasi, dan ketika saya melakukan push_back (std:; move) itu masih memanggil konstruktor salinan. Saya merobek rambut saya di sini.std::move()
salahpush_back()
. Suatu saat ketika Anda mencari begitu keras untuk suatu masalah sehingga Anda tidak melihat kesalahan yang jelas tepat di depan Anda. Dan kemudian saat makan siang dan saya lupa menghapus komentar saya.Menariknya, vektor gcc 4.7.2 hanya menggunakan konstruktor pemindah jika konstruktor pemindah dan destruktornya sama-sama
noexcept
. Contoh sederhana:struct foo { foo() {} foo( const foo & ) noexcept { std::cout << "copy\n"; } foo( foo && ) noexcept { std::cout << "move\n"; } ~foo() noexcept {} }; int main() { std::vector< foo > v; for ( int i = 0; i < 3; ++i ) v.emplace_back(); }
Ini menghasilkan yang diharapkan:
Namun, saat saya menghapus
noexcept
dari~foo()
, hasilnya berbeda:Saya rasa ini juga menjawab pertanyaan ini .
sumber
Tampaknya, satu-satunya cara (untuk C ++ 17 dan awal), untuk menerapkan
std::vector
semantik pemindahan penggunaan pada realokasi adalah menghapus konstruktor salinan :). Dengan cara ini, ia akan menggunakan konstruktor pemindahan Anda atau mati saat mencoba, pada waktu kompilasi :).Ada banyak aturan di mana
std::vector
TIDAK HARUS menggunakan konstruktor bergerak saat realokasi, tetapi tidak ada aturan di mana ia HARUS MENGGUNAKANnya .template<class T> class move_only : public T{ public: move_only(){} move_only(const move_only&) = delete; move_only(move_only&&) noexcept {}; ~move_only() noexcept {}; using T::T; };
Hidup
atau
template<class T> struct move_only{ T value; template<class Arg, class ...Args, typename = std::enable_if_t< !std::is_same_v<move_only<T>&&, Arg > && !std::is_same_v<const move_only<T>&, Arg > >> move_only(Arg&& arg, Args&&... args) :value(std::forward<Arg>(arg), std::forward<Args>(args)...) {} move_only(){} move_only(const move_only&) = delete; move_only(move_only&& other) noexcept : value(std::move(other.value)) {}; ~move_only() noexcept {}; };
Kode langsung
T
Kelas Anda harus memilikinoexcept
operator pemindah / penugasan dannoexcept
penghancur. Jika tidak, Anda akan mendapatkan kesalahan kompilasi.std::vector<move_only<MyClass>> vec;
sumber