Search code examples
c++polymorphism

Why does non-const shared_ptr ref not work polymorphically?


If I want to use shared_ptr polymorphically, why is std::shared_ptr<A>& ptr not working? However, const std::shared_ptr<A>& ptr works fine.

class A {
public:
    virtual void foo() { std::cout << "A" << std::endl; }
};

class B : public A {
public:
    void foo() override { std::cout << "B " << std::endl; }
};

void working(const std::shared_ptr<A>& ptr) {
    ptr->foo();
}

void not_working(std::shared_ptr<A>& ptr) {
    ptr->foo();
}

int main() {
    auto ptr = std::make_shared<B>();
    working(ptr);

    not_working(ptr); // <-- compilation error

    return 0;
}

After compiling, I get the following compilation error:

main.cpp: In function ‘int main()’:
main.cpp:44:16: error: cannot bind non-const lvalue reference of type ‘std::shared_ptr<A>&’ to an rvalue of type ‘std::shared_ptr<A>’
   44 |     notworking(ptr);
      |                ^~~
In file included from /usr/include/c++/11/memory:77,
                 from main.cpp:2:
/usr/include/c++/11/bits/shared_ptr.h:296:9: note:   after user-defined conversion: ‘std::shared_ptr<_Tp>::shared_ptr(const std::shared_ptr<_Yp>&) [with _Yp = B; <template-parameter-2-2> = void; _Tp = A]’
  296 |         shared_ptr(const shared_ptr<_Yp>& __r) noexcept
      |         ^~~~~~~~~~
main.cpp:32:37: note:   initializing argument 1 of ‘void notworking(std::shared_ptr<A>&)’
   32 | void notworking(std::shared_ptr<A>& ptr) {
      |                 ~~~~~~~~~~~~~~~~~~~~^~~
make: *** [<builtin>: main] Error 1

Solution

  • shared_ptr<A> and shared_ptr<B> are distinct and unrelated types. B deriving from A does not make shared_ptr<B> derive from shared_ptr<A>.

    Since B derives from A, there is an implicit conversion from B* to A*, and so the compiler is allowed to assign a shared_ptr<B> object to a shared_ptr<A> object, and they will both be pointing at the same B object.

    When calling working(), the compiler has to create a temporary shared_ptr<A> object from the shared_ptr<B> object. And since a const reference can bind to a temporary, this works as expected.

    When calling not_working(), the compiler still has to create a temporary shared_ptr<A> object. But, since a non-const reference cannot bind to a temporary, you get the error.