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

Передача 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 "Назад к основным словам" .

4b9b3361

Ответ 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, компилятор не мог видеть, что один класс был получен из другого.