Che cosa sta succedendo ?
Quando si crea un Taxi
, si crea anche un Car
oggetto secondario. E quando il taxi viene distrutto, entrambi gli oggetti vengono distrutti. Quando chiami test()
, passi il Car
valore. Quindi un secondo Car
viene copiato e verrà distrutto quandotest()
viene lasciato. Quindi abbiamo una spiegazione per 3 distruttori: il primo e i due ultimi della sequenza.
Il quarto distruttore (che è il secondo nella sequenza) è inaspettato e non ho potuto riprodurlo con altri compilatori.
Può essere solo un temporaneo Car
creato come fonte per l' Car
argomento. Dal momento che non accade quando si fornisce direttamente un Car
valore come argomento, ho il sospetto che sia per trasformarlo Taxi
in Car
. Ciò è inaspettato, poiché esiste già un Car
oggetto secondario in ogni Taxi
. Pertanto, penso che il compilatore non effettui una conversione non necessaria in una temp e non esegua la copia elisione che avrebbe potuto evitare questa temp.
Chiarimento fornito nei commenti:
Qui il chiarimento con riferimento alla norma per l'avvocato linguista per verificare le mie affermazioni:
- La conversione a cui mi riferisco qui è una conversione per costruttore
[class.conv.ctor]
, ovvero la costruzione di un oggetto di una classe (qui Car) basato su un argomento di altro tipo (qui Taxi).
- Questa conversione utilizza quindi un oggetto temporaneo per restituire il suo
Car
valore. Al compilatore sarebbe consentito effettuare una copia elisione secondo [class.copy.elision]/1.1
, poiché invece di costruire un temporaneo, potrebbe costruire il valore da restituire direttamente nel parametro.
- Quindi, se questa temperatura produce effetti collaterali, è perché il compilatore apparentemente non utilizza questa possibile copia-elisione. Non è sbagliato, dal momento che l'elezione della copia non è obbligatoria.
Conferma sperimentale dell'analisi
Ora potrei riprodurre il tuo caso usando lo stesso compilatore e disegnare un esperimento per confermare cosa sta succedendo.
La mia ipotesi di cui sopra era che il compilatore ha selezionato un parametro non ottimale passando il processo, usando la conversione del costruttore Car(const &Taxi)
invece della copia della costruzione direttamente dal Car
oggetto secondario di Taxi
.
Quindi ho provato a chiamare test()
ma esplicitamente lanciando il file Taxi
a Car
.
Il mio primo tentativo non è riuscito a migliorare la situazione. Il compilatore utilizzava ancora la conversione del costruttore non ottimale:
test(static_cast<Car>(taxi)); // produces the same result with 4 destructor messages
Il mio secondo tentativo è riuscito. Fa anche il casting, ma usa il pointer pointer per suggerire fortemente al compilatore di usare l' Car
oggetto secondario Taxi
dell'oggetto e senza creare questo stupido oggetto temporaneo:
test(*static_cast<Car*>(&taxi)); // :-)
E sorpresa: funziona come previsto, producendo solo 3 messaggi di distruzione :-)
Esperimento conclusivo:
In un esperimento finale, ho fornito un costruttore personalizzato per conversione:
class Car {
...
Car(const Taxi& t); // not necessary but for experimental purpose
};
e implementarlo con *this = *static_cast<Car*>(&taxi);
. Sembra sciocco, ma questo genera anche codice che visualizzerà solo 3 messaggi di distruttore, evitando così l'oggetto temporaneo non necessario.
Questo porta a pensare che potrebbe esserci un bug nel compilatore che causa questo comportamento. È una possibilità che in alcune circostanze manchi la possibilità di costruire copie dirette dalla classe base.
Taxi
oggetto a una funzione che prende unCar
oggetto in base al valore?