Optimasi basis kosong sangat bagus. Namun, ia datang dengan batasan berikut:
Optimalisasi basis kosong dilarang jika salah satu kelas basis kosong juga merupakan tipe atau basis tipe anggota data non-statis pertama, karena dua sub-objek dasar dari tipe yang sama diharuskan memiliki alamat yang berbeda dalam representasi objek dari tipe yang paling diturunkan.
Untuk menjelaskan batasan ini, pertimbangkan kode berikut. The static_assert
akan gagal. Sedangkan, mengubah salah satu Foo
atau Bar
sebaliknya mewarisi dari Base2
akan mencegah kesalahan:
#include <cstddef>
struct Base {};
struct Base2 {};
struct Foo : Base {};
struct Bar : Base {
Foo foo;
};
static_assert(offsetof(Bar,foo)==0,"Error!");
Saya mengerti perilaku ini sepenuhnya. Apa yang saya tidak mengerti adalah mengapa perilaku tertentu ada . Jelas ditambahkan karena suatu alasan, karena ini merupakan tambahan eksplisit, bukan pengawasan. Apa alasan untuk ini?
Khususnya, mengapa dua sub-proyek dasar harus memiliki alamat yang berbeda? Di atas, Bar
adalah tipe dan foo
variabel anggota tipe itu. Saya tidak melihat mengapa kelas dasar Bar
penting untuk kelas dasar dari jenis foo
, atau sebaliknya.
Memang, saya jika ada, saya harapkan itu &foo
sama dengan alamat Bar
instance yang berisi itu — seperti yang diperlukan dalam situasi lain (1) . Lagipula, saya tidak melakukan apa-apa dengan virtual
warisan, kelas dasar kosong, dan kompilasi dengan Base2
menunjukkan bahwa tidak ada yang rusak dalam kasus khusus ini.
Tetapi jelas alasan ini tidak benar, dan ada situasi lain di mana batasan ini diperlukan.
Katakanlah jawaban harus untuk C ++ 11 atau lebih baru (Saat ini saya menggunakan C ++ 17).
(1) Catatan: EBO ditingkatkan di C ++ 11, dan khususnya menjadi wajib untuk StandardLayoutType
s (meskipun Bar
, di atas, bukan a StandardLayoutType
).
sumber
Base *a = new Bar(); Base *b = a->foo;
dengana==b
, tetapia
danb
jelas merupakan objek yang berbeda (mungkin dengan metode virtual yang berbeda)Jawaban:
Ok, sepertinya saya salah sepanjang waktu, karena untuk semua contoh saya perlu ada vtable untuk objek dasar, yang akan mencegah pengoptimalan basis kosong untuk memulai. Saya akan membiarkan contoh berdiri karena saya pikir mereka memberikan beberapa contoh menarik mengapa alamat unik biasanya merupakan hal yang baik untuk dimiliki.
Setelah mempelajari keseluruhan ini secara lebih mendalam, tidak ada alasan teknis untuk optimasi kelas dasar kosong untuk dinonaktifkan ketika anggota pertama adalah dari jenis yang sama dengan kelas dasar kosong. Ini hanya properti model objek C ++ saat ini.
Tetapi dengan C ++ 20 akan ada atribut baru
[[no_unique_address]]
yang memberitahu kompiler bahwa anggota data non-statis mungkin tidak memerlukan alamat unik (secara teknis itu berpotensi tumpang tindih [intro.object] / 7 ).Ini menyiratkan bahwa (penekanan milikku)
maka seseorang dapat "mengaktifkan kembali" optimasi kelas dasar kosong dengan memberikan atribut anggota data pertama
[[no_unique_address]]
. Saya menambahkan contoh di sini yang menunjukkan bagaimana ini (dan semua kasus lain yang bisa saya pikirkan) berfungsi.Contoh masalah yang salah melalui ini
Karena tampaknya kelas kosong mungkin tidak memiliki metode virtual, izinkan saya menambahkan contoh ketiga:
Tetapi dua panggilan terakhir adalah sama.
Contoh lama (Mungkin tidak menjawab pertanyaan karena kelas kosong mungkin tidak berisi metode virtual, tampaknya)
Pertimbangkan dalam kode Anda di atas (dengan menambahkan destruktor virtual) contoh berikut
Tetapi bagaimana kompiler harus membedakan kedua kasus ini?
Dan mungkin sedikit kurang dibikin:
Tetapi dua yang terakhir sama jika kita memiliki optimasi kelas dasar kosong!
sumber
std::is_empty
pada cppreference jauh lebih rumit. Sama dari rancangan saat ini di eel.is .dynamic_cast
ketika itu bukan polimorfik (dengan pengecualian kecil tidak relevan di sini).