Donnerstag, Mai 15, 2014

A subtle issue with C++11 unique_ptrs and move semantics

I'm moving the code of an internal tool to C++11 these days, and I stumbled upon a somewhat worrying subtle issue.

C++11 introduces rvalue references and move semantics, which allow the new smart pointer type std::unique_ptr that can be used inside std::vector. One might have some code like this:

std::vector<std::unique_ptr<Foo>> foos;

...

foos.push_back(std::unique_ptr<Foo>(new Foo(...)));


This works just fine, but it's a bit convoluted. Since C++11 introduced both rvalue references and variadic templates, std::vector has a method emplace_back, which looks more convenient. Let's use it:

std::vector<std::unique_ptr<Foo>> foos;

...

foos.emplace_back(new Foo(...));


This works and looks good. However, there is a subtle problem: What happens if the new object of type Foo is constructed, but then the emplace_back needs to allocate more memory, and that allocation fails?

The documentation that I can find states that nothing will happen in this case. Does "nothing" mean that we get a memory leak of one Foo object? Apparently it does - and at least a cursory look into the g++ 4.8 STL seems to confirm this suspicion. It appears that this is a potential memory leak that cannot really be avoided in good-looking code.

What to do about this? It would be nice if emplace_back guaranteed that the desired constructor is called no matter what happens. Unfortunately, such a guarantee would mean constructing the element explicitly in the cleanup path of a bad_alloc exception, which is prone to triggering yet another exception. Seems like this is quite the conundrum...

1 Kommentar:

pal hat gesagt…

hi
somewhat late, but still
in good-looking code you have no new operators. use std::make_unique(...)
it is c++14, but trivial to implement(copy-paste)
std::vector can't take ownership of passed pointer because it assings no special meaning to raw pointers. boost::ptr_vector can