Search code examples
c++c++11type-conversionshared-ptrimplicit-conversion

How does std::shared_ptr converts across class hierarchy when passing by reference?


Looking at 20.8.2.2 Class template shared_ptr [util.smartptr.shared] I realized that std::shared_ptr has template copy constructors and assignment operators that allow the conversion from shared_ptr<Derived> to shared_ptr<Base> if and only if Derived* is convertible to Base*. These conversion are done (in my understanding) only via the templated copy constructors and assignment operators. However, it seems I can also pass a shared_ptr<Derived> to a function that takes shared_ptr<Base>& (i.e., pass by reference). It seems that there should be an implicit conversion operator, but according to the standard there is none.

The code below clarifies what I mean:

#include <iostream>
#include <memory>

struct Base {};
struct Derived: Base {};

void f(const std::shared_ptr<Base>& ) {}

int main()
{
    std::shared_ptr<Derived> spDerived(new Derived);

    // conversion below is OK, via template copy ctor
    std::shared_ptr<Base> spBase(spDerived); 
    // also OK, via template copy assignment operator
    spBase = spDerived; 

    // why is this OK? Cannot see any conversion operators in
    // 20.8.2.2 Class template shared_ptr [util.smartptr.shared]
    f(spDerived); 
}

My question: In this case, who's performing the conversion from shared_ptr<Derived> to shared_ptr<Base> in the call f(spDerived)? (for the compiler shared_ptr<Derived> has no relation whatsoever with shared_ptr<Base>, even if Derived is a child of Base)


Solution

  • A temporary is created via the template copy ctor and that copy is what is referenced. The ctor is implicit so it may be legally called in this situation. Fundamentally there is no difference here between this call and say, f(std::string); f("hello");. The exact same mechanisms are at play. It's a regular implicit conversion.