Passing smart pointers is an important topic which is seldom addressed. This ends with the C++ core guidelines because they have six rules for passing std::shared_ptr and std::unique_ptr.

The six rules violate the import dry (don't repeat yourself) principle for software development. At the end, we have only four rules what makes our life as a software developer a lot easier. Here are the rules.

If a function should take ownership of a Widget, you should take the std::unique_ptr<Widget> by copy. The consequence is that the caller has to move the std::unique_ptr<Widget> to make the code to run.

The call (1) is fine but the call (2) breaks because you can not copy an std::unique_ptr. If your function only wants to use the Widget, it should take its parameter by pointer of by reference. The difference between a pointer and a reference is that a pointer can be a null pointer.

Now, the call (1) fails because you can not bind an rvalue to a non-const lvalue reference. This will not hold for the copy in (2). A lvalue can be bound to an lvalue reference. By the way. The call (0) will not only construct a new Widget(2003), it will also destruct the old Widget(1998).

The next three rules to std::shared_ptr are literally repetitions; therefore, I will make one out of them.

Let's look at each function signature in isolation. What does this mean from the function perspective?

void share(std::shared_ptr<Widget> shaWid): I'm for the lifetime of the function body a shared owner of the Widget. At the begin of the function body, I will increase the reference counter; at the end of the function, I will decrease the reference counter; therefore, the Widget will stay alive, as long as I use it.

void reseat(std::shared_ptr<Widget>& shaWid): I'm not a shared owner of the Widget, because I will not change the reference counter. I have not guaranteed that the Widget will stay alive during the execution of my function, but I can reseat the resource. A non-const lvalue reference is more like: I borrow the resource and can reseat it.

void mayShare(const std::shared_ptr<Widget>& shaWid): I only borrow the resource. Either can I extend the lifetime of the resource nor can I reseat the resource. To be honest, you should use a pointer (Widget*) or a reference (Widget&) as a parameter instead, because there is no added value in using a std::shared_ptr.

globShared (1) is a globally shared pointer. The function shared takes it argument per reference (2). Therefore, the reference counter of shaPtr will no be increased and the function share will no extend the lifetime of Widget(2011). The issue begins with (3). oldFunc accepts a pointer to the Widget; therefore, oldFunc has no guarantee that the Widget will stay alive during its execution. oldFunc only borrows the Widget.

The cure is quite simple. You have to ensure that the reference count of globShared will be increased before the call to the function oldFunc. This means you have to make a copy of the std::shared_ptr:

The same reasoning also applies to std::unique_ptr but I have no simple cure in mind because you can not copy an std::unique_ptr. I suggest you should clone your std::unique_ptr and, therefore, make a new std::unique_ptr.

What's next?

This was the last of my four posts about resource management in the C++ core guidelines. The C++ core guidelines has more than 50 rules for expressions and statements. I will have a closer look at my next post.

Get your e-book at leanpub:

The C++ Standard Library

Concurrency With Modern C++

Get Both as one Bundle

With C++11,C++14, and C++17 we got a lot of new C++ libraries. In addition, the existing ones are greatly improved. The key idea of my book is to give you the necessary information to the current C++ libraries in about 200 pages.

C++11 is the first C++ standard that deals with concurrency. The story goes on with C++17 and will continue with C++20.

I'll give you a detailed insight in the current and the upcoming concurrency in C++. This insight includes the theory and a lot of practice with more the 100 source files.

Get my books "The C++ Standard Library" (including C++17) and "Concurrency with Modern C++" in a bundle.

In sum, you get more than 550 pages full of modern C++ and more than 100 source files presenting concurrency in practice.