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. Jika std::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 dari std::mutex
mereka harus menyimpan jenis mutex asli di heap sehingga akan tetap di lokasi yang sama ketika dipindahkan antar std::mutex
objek atau std::mutex
tidak boleh bergerak. Menyimpannya di heap tidak mungkin, karena a std::mutex
memiliki constexpr
konstruktor dan harus memenuhi syarat untuk inisialisasi konstan (yaitu inisialisasi statis) sehinggastd::mutex
dijamin akan dibangun sebelum eksekusi program dimulai, sehingga konstruktornya tidak dapat menggunakannya new
. Jadi satu-satunya pilihan yang tersisa adalah std::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 untuk std::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 sebuah int
, Anda dapat menyalin nilainya dan kemudian mengaturnya ke nol, tetapi itu masih int
dengan nilai, itu hanya byte memori. Tapi int
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.