Nel seguente codice, viene dichiarato un oggetto wrapper<T>
che contiene uno movable<T>
, dove T
è un tipo incompleto. Il distruttore di movable
viene creato in modo che non possa essere istanziato senza una conoscenza completa di T
, ma il distruttore di wrapper
è solo inoltrato, il che significa che dovrebbe essere sufficiente se ~movable()
viene istanziato al punto di definizione di ~wrapper()
.Perché un costruttore noexcept richiede l'istanziazione del distruttore?
#include <utility>
template<class T>
struct movable {
movable() noexcept = default;
~movable() noexcept { (void) sizeof(T); }
movable(const movable&) noexcept = delete;
movable(movable &&) noexcept = default;
};
template<class T>
class wrapper {
public:
movable<T> m;
wrapper() noexcept = default;
wrapper(wrapper &&) noexcept = default;
~wrapper();
};
struct incomplete;
int main() {
/* extern */ wrapper<incomplete> original;
wrapper<incomplete> copy(std::move(original));
}
Tuttavia, wrapper()
vuole istanziare ~movable()
. Ricevo che in caso di eccezione, la distruzione dei membri deve essere possibile, ma movable()
e wrapper()
sono entrambi senza eccezioni. È interessante notare che la funzione di costruzione delle mosse funziona correttamente (provare a decommentare la parte extern
nel codice di esempio.)
Qual è la ragione di questo comportamento e c'è un modo per aggirare il problema?
compilazione clang fallisce anche quando commentando la 'extern'. – interjay
Hm, "un modello di classe viene istanziato se la completezza del tipo di classe potrebbe influenzare la semantica del programma" ... forse che ha qualcosa a che fare con esso? –
Se una classe ha più membri e durante la costruzione viene lanciata una delle inizializzazioni, le inizializzazioni completate in precedenza devono essere annullate chiamando i distruttori dei rispettivi membri. Penso che quello che vedi sia collegato a questo. – dyp