Jan Rüegg Jan Rüegg - 2 months ago 9
C++ Question

Exception in std::vector<>::emplace_back() safe?

What happens when an exception is thrown in

std::vector<>::emplace_back()
?

For example:

class Foo {
public:
Foo(int bar) {
if (bar == 4) throw std::exception("Something went wrong");
}
}


and

std::vector<std::unique_ptr<Foo>> foo_list;
foo_list.emplace_back(new Foo(3));
try {
foo_list.emplace_back(new Foo(4));
} catch (std::exception error) {
// How bad is it?
}
// Whats inside foo_list now?


I would expect the vector to just contain the first Foo object.

Is this the case? And is this guaranteed by the standard?

And also: Could there be any memory leaks?

Answer

I would expect the vector to just contain the first Foo object.

Is this the case? And is this guaranteed by the standard?

Yes. The comments above already explained that emplace_back never even gets called because the Foo constructor throws while initializing the arguments for the function.

But ...

And also: Could there be any memory leaks?

Yes, you are using the anti-pattern that I have described at Inserting into a container of smart pointers with emplace_back(new X) (also published in Overload Journal #134 - August 2016).

The problem happens when emplace_back needs to reallocate the vector and that fails due to running out of memory. The pointer passed into the function will be lost, and so you leak the Foo object. This can happen for the first insertion (where the Foo constructor doesn't throw):

foo_list.emplace_back(new Foo(3));

Never use emplace_back to insert raw pointers into a container of unique_ptr, instead use make_unique:

foo_list.emplace_back(std::make_unique<Foo>(3));

Or if you have to use C++11 then construct the unique_ptr and insert or emplace that, not a raw pointer:

foo_list.emplace_back(std::unique_ptr<Foo>(new Foo(3)));

This way the object is owned by a unique_ptr immediately, and so if an exception happens inside emplace_back the object will be destroyed correctly.