Apakah aman untuk memanggil penempatan baru pada `ini` untuk objek sepele?

20

Saya tahu bahwa pertanyaan ini sudah ditanyakan beberapa kali tetapi saya tidak dapat menemukan jawaban untuk kasus khusus ini.

Katakanlah saya memiliki kelas sepele yang tidak memiliki sumber daya apa pun dan memiliki destruktor kosong dan konstruktor default. Ini memiliki beberapa variabel anggota dengan inisialisasi di dalam kelas; tidak satu pun dari mereka const.

Saya ingin menginisialisasi ulang dan objek kelas tersebut tanpa menulis deInitmetode dengan tangan. Apakah aman melakukannya seperti ini?

void A::deInit()
{
  new (this)A{};
}

Saya tidak dapat melihat masalah dengan itu - objek selalu dalam keadaan valid, thismasih menunjuk ke alamat yang sama; tapi ini C ++ jadi saya ingin memastikan.

Amomum
sumber
2
Apakah ada anggota objek yang const?
NathanOliver
2
Jika ini valid, apakah akan setara dengan *this = A{};?
Kevin
2
@Amomum *this = A{};berarti, this->operator=(A{});yaitu membuat objek sementara dan menugaskannya *this, mengganti nilai semua anggota data dengan nilai sementara. Karena itu yang Anda inginkan dan (menurut saya) lebih mudah dibaca daripada penempatan yang baru, saya akan menggunakannya.
Kevin
1
@Kevin oh, salahku, kau benar. Daripada - saya pikir itu harus sama jika salinan dihilangkan?
Amomum
1
daripada menjelaskan kelas dengan kata-kata, lebih baik menulis kelas penuh, dan tidak hanya satu metode. lihat sscce.org
BЈовић

Jawaban:

17

Sama halnya dengan legalitas delete this, penempatan baru thisjuga diperbolehkan sejauh yang saya tahu. Juga, mengenai apakah this, atau petunjuk / referensi yang sudah ada sebelumnya dapat digunakan setelah itu, ada beberapa batasan:

[basic.life]

Jika, setelah masa pakai objek berakhir dan sebelum penyimpanan tempat objek yang ditempati digunakan kembali atau dilepaskan, objek baru dibuat di lokasi penyimpanan tempat objek asli ditempati, penunjuk yang menunjuk ke objek asli, referensi yang merujuk ke objek asli, atau nama objek asli akan secara otomatis merujuk ke objek baru dan, sekali masa hidup objek baru telah dimulai, dapat digunakan untuk memanipulasi objek baru, jika:

  • penyimpanan untuk objek baru persis menutupi lokasi penyimpanan tempat objek asli ditempati, dan
  • objek baru dari jenis yang sama dengan objek asli (mengabaikan kualifikasi-tingkat cv), dan
  • tipe objek asli tidak memenuhi syarat const, dan, jika tipe kelas, tidak mengandung anggota data non-statis yang tipenya memenuhi syarat const atau tipe referensi, dan
  • baik objek asli maupun objek baru bukanlah sub-objek yang berpotensi tumpang tindih ([intro.object]).

Dua yang pertama puas dalam contoh ini, tetapi dua yang terakhir perlu dipertimbangkan.

Mengenai poin ketiga, mengingat bahwa fungsinya non-const-kualifikasi, itu harus cukup aman untuk mengasumsikan bahwa objek aslinya adalah non-const. Kesalahan ada di sisi penelepon jika ketegaran telah dibuang. Mengenai const / anggota referensi, saya pikir itu dapat diperiksa dengan menyatakan bahwa ini dapat ditugaskan:

static_assert(std::is_trivial_v<A> && std::is_copy_assignable_v<A>);

Tentu saja, karena penugasan adalah persyaratan, Anda bisa menggunakan *this = {};yang saya harapkan untuk menghasilkan program yang sama. Sebuah use case yang mungkin lebih menarik mungkin adalah menggunakan kembali memori *thisuntuk objek tipe lain (yang akan gagal persyaratan untuk menggunakan this, setidaknya tanpa menafsirkan kembali + pencucian).

Mirip dengan delete this, penempatan baru thishampir tidak dapat digambarkan sebagai "aman".

eerorika
sumber
Menarik. Apakah mungkin untuk memastikan semua kondisi ini dipenuhi dengan static_assert pada beberapa tipe sifat? Tidak yakin apakah ada satu tentang anggota const ...
Amomum
1
@ Amomum Saya tidak berpikir menjadi subobjek adalah sesuatu yang dapat diuji. Anggota const atau referensi akan membuat kelas tidak dapat ditentukan, yang menurut saya tidak dapat terjadi sebaliknya untuk kelas yang sepele.
eerorika
apakah ini berarti pengabaian ketat dilakukan sehubungan dengan keteguhan? dapatkah Anda memberikan contoh di mana ini bisa terjadi?
darune
1
@ Darune Buat objek const, penempatan-baru ke atasnya, gunakan nama asli objek (atau pointer atau referensi yang sudah ada sebelumnya) dan perilaku akan terdefinisi. Efeknya hampir sama dengan optimasi aliasing yang ketat, jika tidak sama sekali.
eerorika
1
Kebalikannya delete ptradalah new T(). Kebalikannya new(ptr)T{}adalah ptr->~T();. stackoverflow.com/a/8918942/845092
Mooing Duck
7

Aturan yang mencakup ini ada di [basic.life] / 5

Suatu program dapat mengakhiri masa hidup dari objek apa pun dengan menggunakan kembali penyimpanan yang ditempati objek atau dengan secara eksplisit memanggil destruktor untuk objek dari tipe kelas. Untuk objek tipe kelas, program tidak diharuskan untuk memanggil destruktor secara eksplisit sebelum penyimpanan yang ditempati objek tersebut digunakan kembali atau dirilis; namun, jika tidak ada panggilan eksplisit ke destruktor atau jika ekspresi-hapus tidak digunakan untuk melepaskan penyimpanan, destruktor tidak secara implisit dipanggil dan program apa pun yang bergantung pada efek samping yang dihasilkan oleh destruktor memiliki perilaku yang tidak ditentukan.

dan [basic.life] / 8

Jika, setelah masa pakai objek berakhir dan sebelum penyimpanan tempat objek yang ditempati digunakan kembali atau dilepaskan, objek baru dibuat di lokasi penyimpanan tempat objek asli ditempati, penunjuk yang menunjuk ke objek asli, referensi yang merujuk ke objek asli, atau nama objek asli akan secara otomatis merujuk ke objek baru dan, sekali masa hidup objek baru telah dimulai, dapat digunakan untuk memanipulasi objek baru, jika:

  • penyimpanan untuk objek baru persis menutupi lokasi penyimpanan tempat objek asli ditempati, dan

  • objek baru dari jenis yang sama dengan objek asli (mengabaikan kualifikasi-tingkat cv), dan

  • tipe objek asli tidak memenuhi syarat const, dan, jika tipe kelas, tidak mengandung anggota data non-statis yang tipenya memenuhi syarat const atau tipe referensi, dan

  • baik objek asli maupun objek baru bukanlah sub-objek yang berpotensi tumpang tindih ([intro.object]).

Karena objek Anda sepele, Anda tidak perlu khawatir tentang [basic.life] / 5 dan selama Anda memenuhi poin-poin dari [basic.life] / 8, maka itu aman.

NathanOliver
sumber