Как выполнить динамический_каст с уникальным_ptr?

У меня есть иерархия классов следующим образом:

class BaseSession : public boost::enable_shared_from_this<BaseSession>
class DerivedSessionA : public BaseSession
class DerivedSessionB : public BaseSession

Внутри производных функций класса я регулярно вызываю такие функции:

Func(boost::dynamic_pointer_cast<DerivedSessionA>(shared_from_this()));

Поскольку я работал с shared_ptr для управления сеансами, это работало нормально. Недавно я обнаружил, что мое использование shared_ptr для этого случая не является оптимальным. Это потому, что эти сеансы - это одноэлементные объекты, которые поддерживают один сокет на каждого клиента. Если сокет снова подключен, копии сеанса стали зомби.

В качестве обходного пути я начал передавать shared_ptr по ссылке, а не по копиям. Это решило проблему зомби.

В идеале я чувствовал, что должен использовать unique_ptr для хранения сеанса, а затем передавать ссылки на другие функции. Это открыло целую банку червей.

Как сделать класс базового класса unique_ptr объектом производного класса unique_ptr? Что такое unique_ptr версия следующей строки?

Func(boost::dynamic_pointer_cast<DerivedSessionA>(shared_from_this()));

Мне просто нужна одна копия объекта сеанса, все остальное должно быть ссылкой.

Ответ 1

Если вы не хотите передать право собственности на свой std::unique_ptr<T>, ваша функция должна взять указатель или ссылку на T.

Таким образом, подпись Func должна быть чем-то вроде Func(DerivedSessionA*)

а затем ваш вызов может выглядеть так:

std::unique_ptr<BaseSession> ptr; // Initialize it with correct value

Func(dynamic_cast<DerivedSessionA*>(ptr.get()));

Или, как вы, кажется, вызываете его непосредственно из метода в BaseSession:

Func(dynamic_cast<DerivedSessionA*>(this));

Ответ 2

Update

Вопрос выяснен:

Извините, я не был ясен. Я хочу, чтобы собственность оставалась с оригинальным владельцем, вызываемая функция должна только ссылаться на нее, а не на владение. Не ищете двух умных указателей для одного и того же объекта.

В этом случае решение просто:

dynamic_cast<B&>(*my_unique_ptr)

Готово. Он бросает, если приведение не выполняется.


Литье shared_ptr

Для shared_ptr существует std::dynamic_pointer_cast<> (http://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast)

Литье unique_ptr

Самый простой способ:

#include <memory>

struct A { virtual ~A() = default; };
struct B : A { };

int main()
{
    std::unique_ptr<A> pa(new B);

    std::unique_ptr<B> pb(dynamic_cast<B*>(pa.release())); // DO NOT DO THIS
}

Как справедливо отмечает комментатор, это может привести к утечке объекта, если преобразование завершилось неудачно. Это не очень полезно.

Причина, по которой dynamic_unique_ptr_cast<> не существует, может заключаться в том, что тип unique_ptr не удаляет дебетер. Было бы сложно/невозможно выбрать подходящее удаление для целевого типа указателя.

Однако для простых случаев вы можете использовать что-то вроде этого:

template <typename To, typename From, typename Deleter> 
    std::unique_ptr<To, Deleter> dynamic_unique_cast(std::unique_ptr<From, Deleter>&& p) {
        if (To* cast = dynamic_cast<To*>(p.get()))
        {
            std::unique_ptr<To, Deleter> result(cast, std::move(p.get_deleter()));
            p.release();
            return result;
        }
        return std::unique_ptr<To, Deleter>(nullptr); // or throw std::bad_cast() if you prefer
    }


auto pb = dynamic_unique_cast<B>(std::move(pa));

Ответ 3

Просто введите сохраненный указатель с помощью метода std::unique_ptr<>::get():

Func(dynamic_cast<DerivedSessionA*>(shared_from_this().get()))

что если shared_from_this() имеет этот прототип:

std::unique_ptr<BaseSession>& shared_from_this();