“Downcasting” unique_ptr a unique_ptr

Ho una serie di fabbriche che restituiscono unique_ptr . Sotto il cofano, tuttavia, forniscono puntatori a vari tipi derivati, ad esempio unique_ptr , unique_ptr , unique_ptr ecc.

Dato DerivedA : Derived and Derived : Base avremmo:

 unique_ptr DerivedAFactory() { return unique_ptr(new DerivedA); } 

Quello che devo fare è “lanciare” il puntatore dal unique_ptr restituito unique_ptr a un livello derivato (non necessariamente quello interno originale). Per illustrare in pseudo codice:

 unique_ptr ptr = static_cast<unique_ptr>(DerivedAFactory()); 

Sto pensando di farlo rilasciando l’object da unique_ptr , quindi utilizzando una funzione che esegue il cast del puntatore raw e lo riassegna a un altro unique_ptr del sapore desiderato (la release verrà esplicitamente eseguita dal chiamante prima della chiamata):

 unique_ptr CastToDerived(Base* obj) { return unique_ptr(static_cast(obj)); } 

È valido o è / ci sarà qualcosa di funky?


PS. C’è una complicazione aggiunta nel fatto che alcune delle fabbriche risiedono in DLL caricate dynamicmente in fase di esecuzione, il che significa che ho bisogno di assicurarmi che gli oggetti prodotti siano distrutti nello stesso contesto (spazio heap) man mano che sono stati creati. Il trasferimento di proprietà (che di solito accade in un altro contesto) deve quindi fornire un deleter dal contesto originale. Ma oltre a dover fornire / lanciare un deleter insieme al puntatore, il problema del cast dovrebbe essere lo stesso.

static_unique_ptr_cast un paio di modelli di funzioni, static_unique_ptr_cast e dynamic_unique_ptr_cast . Usa il primo nei casi in cui sei assolutamente certo che il puntatore sia effettivamente un Derived * , altrimenti usa quest’ultimo.

 template std::unique_ptr static_unique_ptr_cast( std::unique_ptr&& p ) { auto d = static_cast(p.release()); return std::unique_ptr(d, std::move(p.get_deleter())); } template std::unique_ptr dynamic_unique_ptr_cast( std::unique_ptr&& p ) { if(Derived *result = dynamic_cast(p.get())) { p.release(); return std::unique_ptr(result, std::move(p.get_deleter())); } return std::unique_ptr(nullptr, p.get_deleter()); } 

Le funzioni stanno prendendo un riferimento per garantire che non stai tirando fuori il tappeto da sotto i piedi del chiamante rubando il unique_ptr passato a te.