Передача shared_ptr <Производный> как shared_ptr <Base>
Каков наилучший способ передачи shared_ptr
производного типа в функцию, которая принимает shared_ptr
базового типа?
Я обычно передаю shared_ptr
по ссылке, чтобы избежать ненужной копии:
int foo(const shared_ptr<bar>& ptr);
но это не сработает, если я попытаюсь сделать что-то вроде
int foo(const shared_ptr<Base>& ptr);
...
shared_ptr<Derived> bar = make_shared<Derived>();
foo(bar);
Я мог бы использовать
foo(dynamic_pointer_cast<Base, Derived>(bar));
но это кажется субоптимальным по двум причинам:
- A
dynamic_cast
кажется немного чрезмерным для простого производного от производной к основе.
- Как я понимаю,
dynamic_pointer_cast
создает копию (хотя и временную) указателя для передачи функции.
Есть ли лучшее решение?
Обновление для потомков:
Это оказалось проблемой отсутствующего файла заголовка. Кроме того, то, что я пытаюсь сделать здесь, считается антипаттерном. Как правило,
-
Функции, которые не влияют на время жизни объекта (т.е. объект остается в силе в течение всей функции), должны принимать простой указатель или указатель, например. int foo(bar& b)
.
-
Функции, которые потребляют объект (т.е. являются конечными пользователями данного объекта), должны принимать значение unique_ptr
по значению, например. int foo(unique_ptr<bar> b)
. Вызывающие должны std::move
значение в функцию.
-
Функции, которые продлевают время жизни объекта, должны иметь значение shared_ptr
по значению, например. int foo(shared_ptr<bar> b)
. Рекомендуется использовать обычные рекомендации, чтобы избежать круговых ссылок.
Подробнее см. Herb Sutter "Назад к основным словам" .
Ответы
Ответ 1
Хотя Base
и Derived
являются ковариантными, а исходные указатели на них будут действовать соответственно, shared_ptr<Base>
и shared_ptr<Derived>
являются не ковариантными. dynamic_pointer_cast
- это правильный и самый простой способ справиться с этой проблемой.
( Изменить: static_pointer_cast
будет более уместным, потому что вы выполняете отличную от base на основе, что является безопасным и не требует проверок времени выполнения. См. комментарии ниже.)
Однако, если ваша функция foo()
не хочет участвовать в продлении срока службы (или, скорее, участвует в совместном владении объектом), то лучше всего принять a const Base&
и разыменовать shared_ptr
при передаче его на foo()
.
void foo(const Base& base);
[...]
shared_ptr<Derived> spDerived = getDerived();
foo(*spDerived);
В стороне, поскольку типы shared_ptr
не могут быть ковариантными, правила неявных преобразований в ковариантных типах возврата не применяются при возврате типов shared_ptr<T>
.
Ответ 2
Похоже, вы слишком стараетесь. shared_ptr
дешево копировать; это одна из его целей. Передавать их по ссылке не очень много. Если вы не хотите делиться, передайте необработанный указатель.
Тем не менее, есть два способа сделать это, что я могу думать о моей голове:
foo(shared_ptr<Base>(bar));
foo(static_pointer_cast<Base>(bar));
Ответ 3
Также проверьте, что #include
файла заголовка, содержащего полное объявление производного класса, находится в вашем исходном файле.
У меня была эта проблема. std::shared<derived>
не будет отбрасываться на std::shared<base>
. Я просил объявить оба класса, чтобы я мог использовать для них указатели, но поскольку у меня не было #include
, компилятор не мог видеть, что один класс был получен из другого.