21

Consider the following:

std::vector<std::unique_ptr<int>> ptrsToInts;
ptrsToInts.emplace_back(new int);

If reallocation occurs in the vector, and that fails (throwing std::bad_alloc), am I "safe" or will I leak an int?

C++11 23.3.6.5 [vector.modifiers]/1 says:

If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of T or by any InputIterator operation there are no effects.

which seems to indicate that this is a potential problem. That is, if there are "no effects", then no unique_ptr ever was constructed, and therefore the destructor behavior one would rely on to delete that pointer would not occur. (Which might indicate that emplace_back should be banned for containers of unique_ptrs)

Nicol Bolas
  • 378,677
  • 53
  • 635
  • 829
Billy ONeal
  • 97,781
  • 45
  • 291
  • 525
  • 2
    Good point. This has inspired me to drop my use of `new` entirely in favor of `std::make_unique`, so that I can know every allocation ends up on a smart pointer from the get-go. – GManNickG Nov 01 '12 at 21:36
  • I noted this pitfall in a comment here: http://stackoverflow.com/questions/3283778/why-can-i-not-push-back-a-unique-ptr-into-a-vector/3283795#3283795 – James McNellis Nov 01 '12 at 21:45

1 Answers1

19

If reallocation is required and it fails, then yes, your object never went into the container and will thus be lost.

However, it should be noted that this is pure user error. emplace_back should not be "banned" for containers of unique_ptr, because there are perfectly safe ways of doing this (such as reserveing the space beforehand, so you know it will always be there). Also, you can pass in whole unique_ptrs, since it's perfectly capable of using a move constructor.

So really, it's your fault for not properly wrapping your non-RAII object (the int*) in a RAII object before the point where you could throw exceptions.

Nicol Bolas
  • 378,677
  • 53
  • 635
  • 829
  • 2
    Ah -- but the object never made it in. Emplacement constructs the object in-place inside the vector memory block itself. What's being passed in is a pointer, not a `unique_ptr`. Sure, the pointer is destroyed, but do I have the guarantee that the destructor of `unique_ptr` runs and deletes the pointer? – Billy ONeal Nov 01 '12 at 07:43
  • Actually the fact that the `emplace` is a no-op for an exception rather speaks for the `int` not getting `delete`d properly, as its allocation happens completely outside the `emplace`, whereas the construction of the (deallocating) smart pointer happens inside. – Christian Rau Nov 01 '12 at 07:49
  • 1
    Erm, since when does there need to be "fault" assigned here? As for there being "perfectly safe" ways to do this, sure, there are "safe" ways to do it. But if you force use of a move constructor you might as well just use `push_back` instead, because it enforces the correct behavior in all cases. – Billy ONeal Nov 01 '12 at 07:56
  • @BillyONeal: "*But if you force use of a move constructor you might as well just use push_back instead.*" But then you have to pick between the two, rather than always just using `emplace_back`. And who wants the hassle of that? Just use the same one everywhere and make the copy/move explicit. One rule is easier to remember than two. – Nicol Bolas Nov 01 '12 at 07:59
  • 1
    @BillyONeal: As for "fault", you suggested that this should be "banned" (whatever that means). That means you think that the language/API is wrong and should be avoided. Thus, if you encounter into this issue, it is the language/API's fault rather than the programmer. If it's just user error, there's no reason to ban it, right? – Nicol Bolas Nov 01 '12 at 08:01
  • 1
    @Nicol: Hmm... Not what I meant by banned. Let's say "strongly discouraged". – Billy ONeal Nov 01 '12 at 08:02