0

Many programmers advocate the use of make_shared because it reduces typing and reduces programming errors. However there are some cases where using the constructors of shared_ptr is unavoidable. One of these cases is when you have an existing pointer that you want shared_ptr to own, since shared_ptr<Foo>(&existing_ptr) is bad code. Instead, you have to use the unwieldy shared_ptr<Foo>(shared_ptr<Foo>(), p). Not only are you repeating yourself, but you have to create a temporary object.

int main()
{
    using namespace std;

    Foo foo;
    foo.n = 1;
    {
        auto ptr = make_shared<Foo>(move(foo));
        ptr->n = 42;
        cout << ptr->n << " " << foo.n << '\n';
    }
    {
        auto p = &foo;
        auto ptr = shared_ptr<Foo>(shared_ptr<Foo>(), p);
        ptr->n = 42;
        cout << ptr->n << " " << foo.n << '\n';
    }

    return 0;
}
Foo::Foo()
Foo::Foo(Foo &&)
42 1
Foo::~Foo()
42 42
Foo::~Foo()

What's a less verbose way to have shared_ptr own an existing pointer?

2 Answers2

6

The intended use of that constructor is to allow shared pointers to sub objects of shared pointers.

Your use is not the intended use, and is quite dangerous, as you have implicitly created a guarantee that the data you are passing to the shared pointer will last as long as the shared pointer or its copies does, and then failing to enforce that guarantee in any meaningful sense.

If you pass a shared pointer to a function, it has every right to cache a copy if it and use it 15 minutes later. And if you aren't passing a shared pointer to a function, you don't need one.

In general, a function should only require a shared pointer if it intends to extend the lifetime of its argument in a difficult to predict way. So if you have a function that takes a shared pointer and never extends its lifetime (or the lifetime of a pointer to it), it should not be taking a shared pointer. The problem is in the function you are calling, not with how you have to jump through hoops to call it.

Only when you both have a function that is broken, and are unable to fix it and making a copy of Foo on the free store is overly expensive, is your technique worth trying. And that should be extreme corner cases anyhow.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
2

IMO what you're doing there shouldn't be easy because it's very dangerous, and only necessary under highly specialized circumstances where typing a class name twice should be the least of your worries.

But here is a slightly more succinct way to achieve the same thing by using a dummy deleter:

auto ptr = std::shared_ptr<Foo>(&foo, [](void*){});

Also, in your approach it isn't really correct to say that ptr owns foo; rather it owns the null object in the empty std::shared_ptr<Foo> and points to foo (see this answer for a longer discussion). In my code above it does "own" foo in some technical sense (or at least it thinks it does); it's just been prevented from doing anything to it when its reference count reaches zero.

Community
  • 1
  • 1
dlf
  • 9,045
  • 4
  • 32
  • 58