Подтвердить что ты не робот

"Downcasting" unique_ptr <Base> to unique_ptr <Производный>

У меня есть серия заводов, возвращающих unique_ptr<Base>. Однако под капотом они предоставляют указатели на различные производные типы, т.е. unique_ptr<Derived>, unique_ptr<DerivedA>, unique_ptr<DerivedB> и т.д.

Учитывая DerivedA : Derived и Derived : Base, мы получим:

unique_ptr<Base> DerivedAFactory() {
    return unique_ptr<Base>(new DerivedA);
}

Что мне нужно сделать, так это "бросить" указатель из возвращенного unique_ptr<Base> на некоторый производный уровень (не обязательно оригинальный внутренний). Чтобы проиллюстрировать в псевдокоде:

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

Я собираюсь сделать это, выпустив объект из unique_ptr, а затем используя функцию, которая отбрасывает необработанный указатель и переназначает это на другой unique_ptr нужного аромата (явно будет release вызывающим абонентом до вызова):

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

Является ли это действительным, или есть/будет ли что-то напуганное?


PS. Существует дополнительное усложнение в том, что некоторые из фабрик находятся в библиотеках DLL, которые динамически загружаются во время выполнения, а это означает, что я должен убедиться, что созданные объекты были уничтожены в том же контексте (пустое место) по мере их создания. Передача права собственности (как правило, происходит в другом контексте) должна затем предоставлять отсрочку из исходного контекста. Но помимо того, что нужно снабжать/отбрасывать делетера вместе с указателем, проблема кастинга должна быть одинаковой.

4b9b3361

Ответ 1

Я бы создал пару шаблонов функций, static_unique_ptr_cast и dynamic_unique_ptr_cast. Используйте первое в тех случаях, когда вы абсолютно уверены, что указатель на самом деле является Derived *, иначе используйте последний.

template<typename Derived, typename Base, typename Del>
std::unique_ptr<Derived, Del> 
static_unique_ptr_cast( std::unique_ptr<Base, Del>&& p )
{
    auto d = static_cast<Derived *>(p.release());
    return std::unique_ptr<Derived, Del>(d, std::move(p.get_deleter()));
}

template<typename Derived, typename Base, typename Del>
std::unique_ptr<Derived, Del> 
dynamic_unique_ptr_cast( std::unique_ptr<Base, Del>&& p )
{
    if(Derived *result = dynamic_cast<Derived *>(p.get())) {
        p.release();
        return std::unique_ptr<Derived, Del>(result, std::move(p.get_deleter()));
    }
    return std::unique_ptr<Derived, Del>(nullptr, p.get_deleter());
}

Функции берут справочную информацию rvalue, чтобы гарантировать, что вы не вытаскиваете ковер из-под носок вызывающего, украсив переданный вами unique_ptr.