Apa yang terjadi ?
Saat Anda membuat Taxi
, Anda juga membuat Car
subobjek. Dan ketika taksi dihancurkan, kedua benda hancur. Ketika Anda menelepon, test()
Anda melewati nilai Car
dengan. Jadi yang kedua Car
akan dikopi dan akan hancur ketika test()
dibiarkan. Jadi kami memiliki penjelasan untuk 3 destruktor: yang pertama dan yang kedua terakhir secara berurutan.
Destructor keempat (yang kedua dalam urutan) tidak terduga dan saya tidak bisa mereproduksi dengan kompiler lain.
Itu hanya bisa Car
dibuat sementara sebagai sumber untuk Car
argumen. Karena itu tidak terjadi ketika memberikan langsung Car
nilai sebagai argumen, saya menduga itu untuk mengubah ke Taxi
dalam Car
. Ini tidak terduga, karena sudah ada Car
sub proyek di setiap Taxi
. Oleh karena itu saya berpikir bahwa kompiler melakukan konversi yang tidak perlu menjadi temp dan tidak melakukan copy elision yang bisa menghindari temp ini.
Klarifikasi yang diberikan dalam komentar:
Di sini klarifikasi dengan mengacu pada standar pengacara bahasa untuk memverifikasi klaim saya:
- Konversi yang saya maksudkan di sini, adalah konversi oleh konstruktor
[class.conv.ctor]
, yaitu membangun objek dari satu kelas (di sini Mobil) berdasarkan argumen dari tipe lain (di sini Taksi).
- Konversi ini menggunakan objek sementara untuk mengembalikan
Car
nilainya. Kompiler akan diizinkan untuk membuat salinan salinan sesuai [class.copy.elision]/1.1
, karena alih-alih membangun sementara, itu dapat membangun nilai yang akan dikembalikan langsung ke parameter.
- Jadi jika temp ini memberikan efek samping, itu karena kompiler tampaknya tidak menggunakan kemungkinan copy-elision ini. Itu tidak salah, karena salinan elision tidak wajib.
Konfirmasi eksperimental anaysis
Saya sekarang dapat mereproduksi kasing Anda dengan menggunakan kompiler yang sama dan menggambar percobaan untuk mengonfirmasi apa yang sedang terjadi.
Asumsi saya di atas adalah bahwa kompiler memilih proses melewati parameter suboptimal, menggunakan konversi konstruktor Car(const &Taxi)
alih-alih menyalin konstruksi langsung dari Car
subobjek dari Taxi
.
Jadi saya mencoba menelepon test()
tetapi secara eksplisit melemparkan Taxi
ke dalam Car
.
Upaya pertama saya tidak berhasil memperbaiki situasi. Kompiler masih menggunakan konversi konstruktor suboptimal:
test(static_cast<Car>(taxi)); // produces the same result with 4 destructor messages
Upaya kedua saya berhasil. Itu melakukan casting juga, tetapi menggunakan pointer casting agar sangat menyarankan kompiler untuk menggunakan Car
subobjek dari Taxi
dan tanpa membuat objek sementara konyol ini:
test(*static_cast<Car*>(&taxi)); // :-)
Dan mengejutkan: berfungsi seperti yang diharapkan, hanya menghasilkan 3 pesan penghancuran :-)
Eksperimen penutup:
Dalam percobaan terakhir, saya memberikan konstruktor khusus dengan konversi:
class Car {
...
Car(const Taxi& t); // not necessary but for experimental purpose
};
dan mengimplementasikannya dengan *this = *static_cast<Car*>(&taxi);
. Kedengarannya konyol, tetapi ini juga menghasilkan kode yang hanya akan menampilkan 3 pesan destruktor, sehingga menghindari objek sementara yang tidak perlu.
Ini membuat kami berpikir bahwa mungkin ada bug di kompiler yang menyebabkan perilaku ini. Ini adalah kemungkinan salinan-konstruksi langsung dari kelas dasar akan terjawab dalam beberapa keadaan.
Taxi
objek ke fungsi yang mengambilCar
objek dengan nilai?