Qual é o melhor método para passar a shared_ptr
de um tipo derivado para uma função que recebe a shared_ptr
de um tipo base?
Eu geralmente passo shared_ptr
s por referência para evitar uma cópia desnecessária:
int foo(const shared_ptr<bar>& ptr);
mas isso não funciona se eu tentar fazer algo como
int foo(const shared_ptr<Base>& ptr);
...
shared_ptr<Derived> bar = make_shared<Derived>();
foo(bar);
eu poderia usar
foo(dynamic_pointer_cast<Base, Derived>(bar));
mas isso parece abaixo do ideal por dois motivos:
- A
dynamic_cast
parece um pouco excessivo para um elenco simples de derivado para base. - Pelo que entendi,
dynamic_pointer_cast
cria uma cópia (embora temporária) do ponteiro para passar para a função.
Existe uma solução melhor?
Atualização para a posteridade:
Era um problema de falta de um arquivo de cabeçalho. Além disso, o que eu estava tentando fazer aqui é considerado um antipadrão. Geralmente,
Funções que não afetam a vida útil de um objeto (ou seja, o objeto permanece válido durante a função) devem receber uma referência simples ou ponteiro, por exemplo
int foo(bar& b)
.Funções que consomem um objeto (ou seja, são os usuários finais de um determinado objeto) devem assumir um
unique_ptr
valor por, por exemploint foo(unique_ptr<bar> b)
. Os chamadores devemstd::move
inserir o valor na função.As funções que estendem a vida útil de um objeto devem assumir um
shared_ptr
valor por, por exemploint foo(shared_ptr<bar> b)
. O conselho usual para evitar referências circulares se aplica.
Consulte a palestra de volta ao básico de Herb Sutter para obter detalhes.
fonte
shared_ptr
? Por que nenhuma referência constante da barra?dynamic
elenco é necessário apenas para downcasting. Além disso, passar o ponteiro derivado deve funcionar perfeitamente. Ele irá criar um novoshared_ptr
com o mesmo refcount (e aumentá-lo) e um ponteiro para a base, que então se liga à referência const. Uma vez que você já está pegando uma referência, no entanto, não vejo por que você quer fazershared_ptr
isso. Faça umaBase const&
ligaçãofoo(*bar)
.foo(bar)
) não funciona, pelo menos no MSVC 2010.shared_ptr
para passar para a função? Tenho quase certeza de que não há como evitar isso.Respostas:
Embora
Base
eDerived
sejam covariantes, os ponteiros brutos para eles agirão de acordoshared_ptr<Base>
e nãoshared_ptr<Derived>
são covariantes. Esta é a maneira correta e mais simples de lidar com esse problema.dynamic_pointer_cast
( Editar:
static_pointer_cast
seria mais apropriado porque você está transmitindo do derivado para a base, o que é seguro e não requer verificações de tempo de execução. Veja os comentários abaixo.)No entanto, se sua
foo()
função não deseja participar do prolongamento da vida útil (ou, melhor, participar da propriedade compartilhada do objeto), então é melhor aceitarconst Base&
ae desreferenciá-lashared_ptr
ao passá-la parafoo()
.void foo(const Base& base); [...] shared_ptr<Derived> spDerived = getDerived(); foo(*spDerived);
Como um aparte, porque
shared_ptr
tipos não podem ser covariantes, as regras de conversões implícitas entre os tipos de retorno covariante não se aplicam ao retornar tipos deshared_ptr<T>
.fonte
shared_ptr<Derived>
são implicitamente conversíveis emshared_ptr<Base>
, então o código deve funcionar sem travessuras de casting.shared_ptr<Ty>
tem um construtor que leva aeshared_ptr<Other>
faz a conversão apropriada seTy*
for implicitamente conversível paraOther*
. E se um gesso for necessário,static_pointer_cast
é o apropriado aqui, nãodynamic_pointer_cast
.shared_ptr
de para evitar a contagem de referência, então não há realmente nenhuma boa razão para usar umshared_ptr
em primeiro lugar. É melhor usar em seuconst Base&
lugar.static_pointer_cast
, obrigado.Isso também acontecerá se você se esquecer de especificar a herança pública na classe derivada, ou seja, se, como eu, você escrever isto:
class Derived : Base { };
fonte
class
é para parâmetros de modelo;struct
é para definir classes. (Isso é no máximo 45% uma piada.)Parece que você está se esforçando demais.
shared_ptr
é barato para copiar; esse é um de seus objetivos. Repassá-los por referência não faz muito. Se você não quiser compartilhar, passe o ponteiro bruto.Dito isso, há duas maneiras de fazer isso que posso imaginar de início:
foo(shared_ptr<Base>(bar)); foo(static_pointer_cast<Base>(bar));
fonte
shared_ptr
implementação que a Microsoft envia.Verifique também se
#include
o arquivo de cabeçalho que contém a declaração completa da classe derivada está em seu arquivo de origem.Eu tive esse problema. O
std::shared<derived>
não seria lançadostd::shared<base>
. Eu havia declarado as duas classes para que pudesse conter ponteiros para elas, mas, como não tinha o,#include
o compilador não conseguia ver que uma classe era derivada da outra.fonte