Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is shared_ptr counter incremented when passed as const reference to base class?

Why is shared_ptr<drived> counter incremented when I pass it to a function that expects a const shared_ptr<base>&?

In this question one of the answers mentions:

shared_ptr<Base> and shared_ptr<Derived> are not covariant

I suspect that this is relevant to my question. What does it mean that they are not covariant?

Here is a code snippet to show case the scenario:

#include <iostream>
#include <memory>

class Base {};

class Derived : public Base {};

void f(const std::shared_ptr<Base>& x)
{
    std::cout << "in function expecting const shared_ptr<Base>& - Use count: " << x.use_count() << std::endl;
}

int main(int argc, char const *argv[])
{
    std::cout << "Base class" << std::endl;
    auto a = std::make_shared<Base>();
    std::cout << "Created shared_ptr:  Initial use count: " << a.use_count() << std::endl;
    f(a);

    std::cout << "------------------\nChild class" << std::endl;
    auto b = std::make_shared<Derived>();
    std::cout << "Created shared_ptr. Initial use count: " << b.use_count() << std::endl;
    f(b);

    return 0;
}

Results in:

>> g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
Base class
Created shared_ptr:  Initial use count: 1
in function expecting const shared_ptr<Base>& - Use count: 1
------------------
Child class
Created shared_ptr. Initial use count: 1
in function expecting const shared_ptr<Base>& - Use count: 2
like image 442
pooya13 Avatar asked Mar 03 '23 19:03

pooya13


1 Answers

A shared_ptr<Derived> is not a shared_ptr<Base>. They are completely different types.

In order to get a shared_ptr<Base> from a shared_ptr<Derived> you need to create one. The compiler can put in a call to the constructor because it isn't marked explicit. This will increase the use count because they share ownership.

template< class Y > shared_ptr( const shared_ptr<Y>& r ) noexcept;

Constructs a shared_ptr which shares ownership of the object managed by r. If r manages no object, *this manages no object too. The template overload doesn't participate in overload resolution if Y* is not implicitly convertible to (until C++17) compatible with (since C++17) T*.

You can see for yourself that a new shared_ptr is created by changing f() to take a non-const reference. The compiler should give you an error because you can't bind a temporary to a non-const reference. See here

like image 122
Kevin Avatar answered Mar 06 '23 07:03

Kevin