C++11: using unique_ptr with standard library containers
June 20th, 2012 at 8:46 pmBefore C++11, the only "smart" pointer available in the standard C++ library was auto_ptr. Alas, auto_ptr isn’t very smart. It has very problematic copy semantics that make it difficult to use in several important scenarios. In particular, auto_ptr can not be used with standard containers and algorithms!
To quote from Herb Sutter:
auto_ptr is most charitably characterized as a valiant attempt to create a unique_ptr before C++ had move semantics.
auto_ptr is now deprecated, and should not be used in new code. When you get a chance, try doing a global search-and-replace of auto_ptr to unique_ptr in your code base
So what is this unique_ptr thing, and what can it be used for?
Basic capabilities
To put it simply, unique_ptr should be the default smart pointer used by new C++ code, replacing "raw" pointers as much as possible. unique_ptr cleanly represents the single ownership idiom – it cannot be copied and assigned, and it cleans up the pointed object when it’s destructed.
Here’s some code to demonstrate this [1]:
#include <iostream>
#include <cstdlib>
#include <memory>
using namespace std;
struct Foo {
Foo() {cerr << "Foo [" << this << "] constructed\n";}
virtual ~Foo() {cerr << "Foo [" << this << "] destructed\n";}
};
int main(int argc, char** argv) {
// .. some code
{
unique_ptr<Foo> fp(new Foo());
unique_ptr<Foo> fp2(fp); // ERROR! can't copy unique_ptr
unique_ptr<Foo> fp3;
fp3 = fp; // ERROR! can't assign unique_ptr
cerr << "Exiting scope\n";
} // fp will be destroyed, and will destruct the pointed object
return 0;
}
The lines marked with the ERROR! comment won’t actually compile. The compiler will complain saying something like:
error: use of deleted function
'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&)
If these two lines are commented out, the code will print:
Foo [0x845010] constructed
Exiting scope
Foo [0x845010] destructed
In addition to managing the pointed object’s lifetime, unique_ptr provides the other expected capabilities of a smart pointer: it overloads operator* and operator->, provides a means to obtain the raw pointer (get), to relinquish control of the pointed object (release), and to replace the object it manages (reset). It also lets you customize the way the pointed object is deleted (if you don’t want it to be the default delete operator), and has some other niceties – just consult your favorite C++ reference.
What about sources and sinks?
In this article I want to focus not on the grocery list of unique_ptr‘s features, but its interesting move semantics. Specifically, given that unique_ptr forbids copying and assignment, one may wonder how it can fit in the source and sink idiom which is so useful for smart pointers.
In other words, we’d like this to work:
// source creates a Foo object, wraps it in a smart pointer for safety
// and provides the result to the caller, giving it the ownership of the
// object in the process.
unique_ptr<Foo> source();
// sink gets a Foo object wrapped in a smart pointer for safety. It also
// assumes ownership of the provided object.
void sink(unique_ptr<Foo> p);
And in C++11, it does! Even though unique_ptr can’t be copied, it can be moved. Move semantics are a perfect match for unique_ptr – the two concepts reinforce each other. With move semantics, unique_ptr is both safe and efficient. Here’s some code to demonstrate this:
#include <iostream>
#include <cstdlib>
#include <memory>
using namespace std;
struct Foo {
Foo() {cerr << "Foo [" << this << "] constructed\n";}
virtual ~Foo() {cerr << "Foo [" << this << "] destructed\n";}
};
void sink(unique_ptr<Foo> p) {
cerr << "Sink owns Foo [" << p.get() << "]\n";
}
unique_ptr<Foo> source() {
cerr << "Creating Foo in source\n";
return unique_ptr<Foo>(new Foo);
}
int main(int argc, char** argv) {
cerr << "Calling source\n";
unique_ptr<Foo> pmain = source(); // Can also be written as
// auto pmain = source();
cerr << "Now pmain owns Foo [" << pmain.get() << "]\n";
cerr << "Passing it to sink\n";
sink(pmain); // ERROR! can't copy unique_ptr
sink(move(pmain)); // OK: can move it!
cerr << "Main done\n";
return 0;
}
Again, there’s a line marked with ERROR! here – it demonstrates once again that a unique_ptr can’t be copied. However, it can be explicitly moved, as the next line shows [2]. When the erroneous line is commented out, this code prints:
Calling source
Creating Foo in source
Foo [0x1767010] constructed
Now pmain owns Foo [0x1767010]
Passing it to sink
Sink owns Foo [0x1767010]
Foo [0x1767010] destructed
Main done
Note how cleanly the ownership is being passed between the functions in this code. At each point in time, only a single unique_ptr owns the pointed Foo object. Moreover, this is efficient – the actual pointed object only gets constructed once and destructed once.
Containers – motivation
So unique_ptr is a useful single-ownership smart pointer. But what makes it really shine (especially when compared to auto_ptr) is that it can be used in standard containers.
Why is it so important to be able to place smart pointers into containers? Because holding objects by value is sometimes very expensive. Containers, especially when coupled with algorithms, tend to move objects around. Large objects are expensive to copy, hence we’d like to keep pointers to objects inside containers instead.
What follows is a very simplistic example that demonstrates this. It shows how much more expensive it is to sort a vector of large objects that are stored by value, than it is when they’re stored by pointer [3].
First, let’s create a synthetic "large" object that has well defined ordering properties by some numeric ID:
struct SomeLargeData {
SomeLargeData(int id_)
: id(id_)
{}
int id;
int arr[100];
};
We also need a function to compare two such objects. Actually, we need two – one for a container that holds object by value, and another for the by-pointer version:
bool compare_by_value(const SomeLargeData& a, const SomeLargeData& b) {
return a.id < b.id;
}
bool compare_by_ptr(const SomeLargeData* a, const SomeLargeData* b) {
return a->id < b->id;
}
Let’s now create two vectors and populate them with random objects:
vector<SomeLargeData> vec_byval;
vector<SomeLargeData*> vec_byptr;
for (int i = 0; i < n; ++i) {
int id = rand() % 500000;
vec_byval.push_back(SomeLargeData(id));
vec_byptr.push_back(new SomeLargeData(id));
}
Finally, we’ll sort the two vectors with the standard sort algorithm, and measure the runtime for some large n:
sort(vec_byval.begin(), vec_byval.end(), compare_by_value);
sort(vec_byptr.begin(), vec_byptr.end(), compare_by_ptr);
The timing results I get are quite consistent – the by-pointer sorting is 2-3x faster than the by-value sorting [4]. That’s a very significant difference, and it’s all due to the copying sort has to do for moving the objects around inside the container.
So holding objects of non-trivial size inside standard containers is not a good idea in terms of performance. But holding raw pointers to them is also not so great, because of all the safety issues that come with raw pointers. The container can’t own the pointed objects because its destructor will just "destruct" the pointer, which does nothing. So the calling code has to own the actual objects which are being shuffled around by the container. Add exceptions and/or early returns to the mix, and this is a recipe for memory leaks or even worse problems.
What we’d really like to do is let our objects be managed by a smart pointer and put that into a container. This would guarantee a clean ownership strategy – the container destroys its contents when it gets destroyed itself – just the way it should be. This is why unique_ptr is so exciting.
Containers of unique_ptr
Adapting the by-pointer version of the code above to hold unique_ptr is very simple. First, we need another comparison function:
bool compare_by_uniqptr(const unique_ptr<SomeLargeData>& a,
const unique_ptr<SomeLargeData>& b) {
return a->id < b->id;
}
And then we just need to create the vector, populate it and then sort it, similarly to the way we’ve done for the other vectors:
vector<unique_ptr<SomeLargeData>> vec_byuniqptr;
for (int i = 0; i < n; ++i) {
int id = rand() % 500000;
// ...
vec_byuniqptr.push_back(
unique_ptr<SomeLargeData>(new SomeLargeData(id)));
}
sort(vec_byuniqptr.begin(), vec_byuniqptr.end(), compare_by_uniqptr);
That’s it! And the performance? Almost identical to the by-pointer version (I measured differences of 1-5%, depending on the data).
Conclusion
I have mentioned how rvalue references and move semantics can make code more efficient with C++11. unique_ptr is another great example that makes me want to use a C++11-capable compiler as soon as possible.
unique_ptr provides an excellent mix of efficiency and safe memory management. IMHO it’s a great example of how several ideas in language design interact to create a whole that is larger than its parts.

| [1] | C++11 support in compilers and standard library implementations varies. To make all the code in this article work, I installed the latest gcc (4.7.1) from source on my Ubuntu box. It didn’t work with 4.5, I don’t know about 4.6. |
| [2] | It can also be taken from an rvalue: sink(unique_ptr<Foo>(new Foo)) would work, because rvalue references can be moved directly. |
| [3] | I don’t claim this is good design – it’s just a synthetic example created to demonstrate a point. |
| [4] | The speedup grows as the size of the object grows. Increasing the arr member to hold 1000 integers makes the speedup 10x. |
| [5] | For the sorting benchmark demonstrated in this article shared_ptr is about 10% slower than unique_ptr. As for size, while the size of unique_ptr is exactly the size of a raw pointer, shared_ptr is about twice as large. |
Related posts:

June 21st, 2012 at 08:55
I’m not sure I agree with the spirit of the article:
1) Bare pointer have its uses. As you said, unique pointer means passing ownership, bare pointer means you can use it, but you’re not allowed to keep it, and you’re not responsible for disposing it.
The good rule of a thumb IMHO, is not “don’t use bare pointers”, but “never write delete in new code”.
2) The best practice is not “build huge object and fix it by unique pointers”, but “build sensible objects and pass them by value”. So for instance SomeLargeData will hold a vector instead of plain array. Then, you can simply store it in the vector, and all will work well.
I’m not saying of course that unique_ptr is not useful, on the contrary, it’s immensely useful, but it is not the only way to move things efficiently in C++11.
June 21st, 2012 at 09:55
Elazar,
Not sure what exactly you don’t agree with. It didn’t say to never use raw pointers – I agree they have their uses, especially for “borrowed” references in some cases. Also, I explicitly said to take the container example with a grain of salt as it was synthetically built to demonstrate the point. Yes, it is best to avoid huge objects, but that’s not always possible. Even if the object isn’t just a holder of some array/vector, it may have a few fields that make it considerably larger than a pointer. In such cases, sorting via a pointer makes sense.
June 21st, 2012 at 18:05
1) What I bothered me was “…replacing “raw” pointers as much as possible.”, but I guess we both agree that unique_ptr is the default over shared_ptr.
2) I don’t think a few fields using unique_ptr would increase performance. A vector of class with 6 pointers ran as fast as its unique_ptr brother:
http://ideone.com/shjPr
June 25th, 2012 at 14:48
Can’t we get the best of all worlds, by doing this:
This gives us fast by-pointer sorting AND ownership of the pointed objects. So I don’t get the excitement about unique_ptr.
June 26th, 2012 at 12:44
Interesting article, thanks, but I’m wondering about one thing: What guarantee do we have that sort() can sort unique_ptrs? Consider that any algorithm that can produce multiple copies of a given element (e.g. fill()) is necessarily incompatible with unique_ptr element types; only algorithms that result in at most 1 copy of each original element can possibly be implemented compatibly. The fact that sorting a container meets the latter criterion (it leaves exactly 1 copy of each element) means that it’s clearly *possible* to implement sort() so that it can work on unique_ptr elements (e.g. it *could* be implemented using only swap()s on elements, never assignments), but equally it’s possible that it’s implemented incompatibly (e.g. it might internally use “vanilla” assignments or copy constructor calls on the element type). In fact it seems likely that it would be implemented these incompatible calls, since doing so would be (a constant factor) faster in some cases!
So: Does the standard guarantee that sort() (and other similar algorithms, like remove()) will be able to handle unique_ptrs? Or was it just a lucky fluke of the particular C++ Standard Library implementation you used that your example code compiled and ran?
Thanks!
June 26th, 2012 at 14:39
j_random_hacker,
I assume that the C++11 standard library is required to implement appropriate move semantics in relevant algorithms.
August 13th, 2012 at 15:16
I wonder how,
unique_ptr source() works, Shouldn’t it be unique_ptr& source(). In the former, it is a return by value and I wonder how this works as you described two lines later that passing pmain to sink in sink(pmain) doesn’t work as it can’t be copied as it is pass by value.
August 27th, 2012 at 08:24
PS, implicit move semantics
November 5th, 2012 at 12:22
Hi
Nice article. But my results are different from what you wrote.
I implemented your code using gcc 4.7 (g++ (Ubuntu/Linaro 4.7.2-4precise1) 4.7.2) on my desktop (OS xubuntu 12.04, Intel(R) Core(TM)2 Duo CPU, E8400 @ 3.00GHz).
I build a vector of 2010000 containing SomeLargeData objects, raw pointers to SomeLargeData objects and unique-pointers to SomeLargeData objects. The results are that raw pointer container is the faster to sort (as expected) but the container with the unique pointers is the slower!!!
Here a dump of my tests
By value test
TIMESTAMP-START 11:18:23:127277 (ms ~ 40703127)
TIMESTAMP-END 11:18:27:378556 (ms ~ 40707378)
ELAPSED TIME (ms) 4251
By pointer test
TIMESTAMP-START 11:18:27:378658 (ms ~ 40707378)
TIMESTAMP-END 11:18:29:841735 (ms ~ 40709841)
ELAPSED TIME (ms) 2463
By unique_pointer test
TIMESTAMP-START 11:18:29:841816 (ms ~ 40709841)
TIMESTAMP-END 11:18:35:674064 (ms ~ 40715674)
ELAPSED TIME (ms) 5833
What do you think? The overhead of the unique-ptr over the raw pointer is so heavy?
Regards
westfork
November 6th, 2012 at 14:51
westfork,
Strange indeed. I would try with a different compiler, maybe the specific version you have has a flawed implementation of
unique_ptr.March 19th, 2013 at 11:58
@Fan The first time the value vector grows it will invalidate all the pointers in the pointer vector. Since it never tells you when this happens, but can happen on any insertion, you have to assume that the pointer is invalid after the next insertion.
@Westfork I would guess that the implementation just hasn’t had enough time to mature yet. There must be at least as much over with the unique_ptr as raw pointers, but double does seem excessive. Did you remember to delete in the raw pointer example?