Saya terkejut ini tidak muncul di hasil pencarian saya, saya pikir seseorang akan menanyakan ini sebelumnya, mengingat kegunaan semantik bergerak di C ++ 11:
Kapan saya harus (atau apakah sebaiknya saya) membuat kelas tidak dapat dipindahkan di C ++ 11?
(Alasan lain selain masalah kompatibilitas dengan kode yang ada, yaitu.)
c++
c++11
move-semantics
c++-faq
pengguna541686
sumber
sumber
+1
dari saya) dengan jawaban yang sangat menyeluruh dari Herb (atau kembarannya, sepertinya ), jadi saya menjadikannya sebagai entri FAQ. Jika seseorang keberatan hanya ping saya di ruang tunggu , jadi ini bisa dibahas di sana.T x = std::move(anotherT);
legal" tidak setara. Yang terakhir adalah permintaan pindah yang mungkin jatuh kembali ke ctor salinan jika T tidak memiliki ctor bergerak. Jadi, apa sebenarnya arti "bergerak"?Jawaban:
Jawaban Herb (sebelum diedit) benar-benar memberi contoh yang baik dari jenis yang seharusnya tidak menjadi bergerak:
std::mutex
.Jenis mutex asli OS (misalnya
pthread_mutex_t
pada platform POSIX) mungkin bukan "invarian lokasi" yang berarti alamat objek adalah bagian dari nilainya. Misalnya, OS mungkin menyimpan daftar pointer ke semua objek mutex yang diinisialisasi. Jikastd::mutex
berisi jenis mutex OS asli sebagai anggota data dan alamat jenis asli harus tetap diperbaiki (karena OS menyimpan daftar penunjuk ke mutexnya) maka salah satu daristd::mutex
mereka harus menyimpan jenis mutex asli di heap sehingga akan tetap di lokasi yang sama ketika dipindahkan antarstd::mutex
objek ataustd::mutex
tidak boleh bergerak. Menyimpannya di heap tidak mungkin, karena astd::mutex
memilikiconstexpr
konstruktor dan harus memenuhi syarat untuk inisialisasi konstan (yaitu inisialisasi statis) sehinggastd::mutex
dijamin akan dibangun sebelum eksekusi program dimulai, sehingga konstruktornya tidak dapat menggunakannyanew
. Jadi satu-satunya pilihan yang tersisa adalahstd::mutex
menjadi tidak tergoyahkan.Alasan yang sama berlaku untuk jenis lain yang berisi sesuatu yang membutuhkan alamat tetap. Jika alamat sumber daya harus tetap, jangan pindahkan!
Ada argumen lain untuk tidak bergerak,
std::mutex
yaitu akan sangat sulit melakukannya dengan aman, karena Anda perlu tahu bahwa tidak ada yang mencoba mengunci mutex pada saat mutex sedang dipindahkan. Karena mutex adalah salah satu blok bangunan yang dapat Anda gunakan untuk mencegah balapan data, akan sangat disayangkan jika mereka tidak aman terhadap balapan itu sendiri! Dengan immovable,std::mutex
Anda tahu satu-satunya hal yang dapat dilakukan siapa pun padanya setelah dibuat dan sebelum dihancurkan adalah dengan menguncinya dan membukanya, dan operasi tersebut secara eksplisit dijamin aman untuk thread dan tidak memperkenalkan data race. Argumen yang sama ini berlaku untukstd::atomic<T>
objek: kecuali mereka dapat dipindahkan secara atomis, tidak mungkin untuk memindahkannya dengan aman, utas lain mungkin mencoba memanggilcompare_exchange_strong
pada objek tepat pada saat itu sedang dipindahkan. Jadi kasus lain di mana jenis tidak boleh dipindahkan adalah di mana mereka adalah blok bangunan tingkat rendah dari kode bersamaan yang aman dan harus memastikan atomisitas dari semua operasi pada mereka. Jika nilai objek dapat dipindahkan ke objek baru kapan saja, Anda perlu menggunakan variabel atom untuk melindungi setiap variabel atom sehingga Anda tahu apakah aman untuk menggunakannya atau telah dipindahkan ... dan variabel atom untuk melindungi variabel atom itu, dan seterusnya ...Saya pikir saya akan menggeneralisasi untuk mengatakan bahwa ketika sebuah objek hanyalah sepotong memori murni, bukan jenis yang bertindak sebagai pemegang nilai atau abstraksi nilai, tidak masuk akal untuk memindahkannya. Tipe mendasar seperti
int
tidak bisa bergerak: memindahkannya hanyalah salinan. Anda tidak dapat merobek nyali dari sebuahint
, Anda dapat menyalin nilainya dan kemudian mengaturnya ke nol, tetapi itu masihint
dengan nilai, itu hanya byte memori. Tapiint
masih bisa dipindahkandalam istilah bahasa karena salinan adalah operasi pemindahan yang valid. Namun untuk jenis yang tidak dapat disalin, jika Anda tidak ingin atau tidak dapat memindahkan bagian memori dan Anda juga tidak dapat menyalin nilainya, maka itu tidak dapat dipindahkan. Mutex atau variabel atom adalah lokasi memori tertentu (diperlakukan dengan properti khusus) sehingga tidak masuk akal untuk dipindahkan, dan juga tidak dapat disalin, jadi tidak dapat dipindahkan.sumber
Jawaban singkat: Jika suatu jenis dapat disalin, ia juga harus dapat dipindahkan. Namun, kebalikannya tidak benar: beberapa tipe seperti
std::unique_ptr
dapat dipindahkan namun tidak masuk akal untuk menyalinnya; ini secara alami adalah tipe yang hanya bergerak.Jawaban yang sedikit lebih panjang mengikuti ...
Ada dua jenis tipe utama (di antara yang lebih bertujuan khusus seperti ciri):
Jenis nilai seperti, seperti
int
atauvector<widget>
. Ini mewakili nilai-nilai, dan secara alami harus dapat disalin. Dalam C ++ 11, secara umum Anda harus memikirkan pemindahan sebagai pengoptimalan salinan, sehingga semua jenis yang dapat disalin secara alami harus dapat dipindah ... pemindahan hanyalah cara yang efisien untuk melakukan penyalinan dalam kasus yang sering umum yang tidak Anda lakukan ' Saya tidak membutuhkan objek aslinya lagi dan hanya akan menghancurkannya.Tipe mirip referensi yang ada dalam hierarki pewarisan, seperti kelas dasar dan kelas dengan fungsi anggota yang dilindungi atau virtual. Ini biasanya dipegang oleh penunjuk atau referensi, sering kali
base*
ataubase&
, dan karenanya tidak menyediakan konstruksi salinan untuk menghindari pemotongan; jika Anda ingin mendapatkan objek lain seperti yang sudah ada, Anda biasanya memanggil fungsi virtual seperticlone
. Ini tidak memerlukan konstruksi pemindahan atau penugasan karena dua alasan: Mereka tidak dapat disalin, dan mereka sudah memiliki operasi "pemindahan" alami yang lebih efisien - Anda cukup menyalin / memindahkan penunjuk ke objek dan objek itu sendiri tidak harus pindah ke lokasi memori baru sama sekali.Sebagian besar jenis termasuk dalam salah satu dari dua kategori itu, tetapi ada jenis jenis lain juga yang juga berguna, hanya lebih jarang. Secara khusus di sini, jenis yang mengekspresikan kepemilikan unik suatu sumber daya, seperti
std::unique_ptr
, secara alami merupakan jenis yang hanya bergerak, karena tidak seperti nilai (tidak masuk akal untuk menyalinnya) tetapi Anda menggunakannya secara langsung (tidak selalu dengan penunjuk atau referensi) dan ingin memindahkan objek jenis ini dari satu tempat ke tempat lain.sumber
std::mutex
tidak tergoyahkan, karena mutex POSIX digunakan oleh alamat.Sebenarnya ketika saya mencari-cari, saya menemukan beberapa tipe di C ++ 11 tidak dapat dipindahkan:
mutex
jenis (recursive_mutex
,timed_mutex
,recursive_timed_mutex
,condition_variable
type_info
error_category
locale::facet
random_device
seed_seq
ios_base
basic_istream<charT,traits>::sentry
basic_ostream<charT,traits>::sentry
atomic
tipeonce_flag
Rupanya ada diskusi di Clang: https://groups.google.com/forum/?fromgroups=#!topic/comp.std.c++/pCO1Qqb3Xa4
sumber
iterators / iterator adaptors
harus diedit karena C ++ 11 memiliki move_iterator?std::reference_wrapper
. Oke, yang lainnya memang sepertinya tidak bisa dipindahkan.ios_base
,type_info
,facet
), 3. berbagai macam hal-hal aneh (sentry
). Mungkin satu-satunya kelas yang tidak dapat digerakkan yang akan ditulis oleh programmer rata-rata ada di kategori kedua.Alasan lain yang saya temukan - kinerja. Katakanlah Anda memiliki kelas 'a' yang memiliki nilai. Anda ingin mengeluarkan antarmuka yang memungkinkan pengguna mengubah nilai untuk waktu terbatas (untuk cakupan).
Cara untuk mencapai ini adalah dengan mengembalikan objek 'scope guard' dari 'a' yang mengembalikan nilainya ke destruktornya, seperti:
Jika saya membuat change_value_guard dapat dipindahkan, saya harus menambahkan 'jika' ke destruktornya yang akan memeriksa apakah penjaga telah dipindahkan - itu adalah tambahan jika, dan berdampak pada kinerja.
Ya, tentu, itu mungkin dapat dioptimalkan oleh pengoptimal yang waras, tetapi tetap bagus bahasanya (ini membutuhkan C ++ 17 meskipun, untuk dapat mengembalikan jenis yang tidak dapat dipindahkan membutuhkan penghapusan salinan yang dijamin) tidak memerlukan kami untuk membayar itu jika kita tidak akan memindahkan penjaga selain mengembalikannya dari fungsi pembuatan (prinsip jangan-bayar-untuk-apa-jangan-gunakan).
sumber