Qualcosa mi dà fastidio:
MyClass& operator=(const MyClass& other)
{
MyClass tmp(other);
swap(tmp);
return *this;
}
Primo, leggere la parola "scambia" quando la mia mente pensa "copia" irrita il mio buon senso. Inoltre, metto in dubbio l'obiettivo di questo trucco stravagante. Sì, eventuali eccezioni nella costruzione delle nuove risorse (copiate) dovrebbero verificarsi prima dello scambio, il che sembra un modo sicuro per assicurarsi che tutti i nuovi dati siano riempiti prima di renderli disponibili.
Va bene. Allora, che dire delle eccezioni che si verificano dopo lo scambio? (quando le vecchie risorse vengono distrutte quando l'oggetto temporaneo esce dall'ambito) Dal punto di vista dell'utente dell'assegnazione, l'operazione è fallita, tranne che non l'ha fatto. Ha un enorme effetto collaterale: la copia è realmente avvenuta. Solo una pulizia delle risorse non è riuscita. Lo stato dell'oggetto di destinazione è stato alterato anche se l'operazione sembra dall'esterno non riuscita.
Quindi, propongo invece di "scambiare" per fare un "trasferimento" più naturale:
MyClass& operator=(const MyClass& other)
{
MyClass tmp(other);
transfer(tmp);
return *this;
}
C'è ancora la costruzione dell'oggetto temporaneo, ma la prossima azione immediata è liberare tutte le risorse correnti della destinazione prima di spostare (e annullare in modo che non vengano liberate due volte) le risorse della sorgente su di esso.
Invece di {costruisci, sposta, distruggi}, propongo {costruisci, distruggi, sposta}. La mossa, che è l'azione più pericolosa, è quella presa per ultima dopo che tutto il resto è stato risolto.
Sì, il fallimento della distruzione è un problema in entrambi gli schemi. I dati sono danneggiati (copiati quando non pensavi che fossero) o persi (liberati quando non pensavi che lo fossero). Perso è meglio che danneggiato. Nessun dato è meglio di dati errati.
Trasferisci invece di scambiare. Questo è comunque il mio suggerimento.