Search code examples
c++stlmovervaluebad-alloc

bad_alloc upon inserting object by rvalue and object validity


Consider the following code (assume that SomeClass has move constructor and fields like pointers which would go invalid after object gets its content swaped with another object):

SomeClass data;
std::list queue;
try { queue.push_back(std::move(data)); }
catch(std::bad_alloc)
{
    data.doThingsUsingObjectData(); //Can I do this here?
    return;
}
data.doThingsUsingObjectData(); //Definitelly can't do this here - object was invalidated

The point is - if bad_alloc is thrown upon adding object to STL with std::move(), would that mean the object I tried to add to STL by rvalue is actualy still valid and I can access it without worrying about undefined behaviour?


Solution

  • A std::list stores its elements in nodes. When you add an element a new node is allocated, then the element is stored in the node (via a call to std::allocator_traits<A>::construct, which typically does a placement-new to construct the object in place inside the node), and then finally the node is added to the list, by updating the pointers of the new node and the adjacent node(s) to link it into the list.

    If a bad_alloc exception is thrown it can only be from allocating the new node, or from constructing the element inside the node (because updating the pointers won't throw anything).

    If the exception happens when allocating the node object then it is very likely that the object has not been moved from yet, because no attempt has been made to move-construct a new element (you can't do that until you have a node to construct it into!). There's no guarantee that the container doesn't create intermediate variables before constructing the final element inside the node, but that would be a poor quality implementation, as there is no reason to do that.

    If the class has a non-throwing move constructor then you know that the exception must have come from the node allocation. Otherwise, if the exception comes from constructing the element then the state of the rvalue argument will be dictated by the exception-safety guarantees of the class in question.

    So in short, if the std::list implementation is bad, or the object being inserted can throw during move construction and doesn't have the strong exception-safety guarantee, then the rvalue argument might be left in a moved-from state. But otherwise, you should be able to rely on it being unmodified.