3

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?

PookyFan
  • 785
  • 1
  • 8
  • 23
  • I do not think that move makes object invalid. – Slava Apr 29 '16 at 13:48
  • Okay, I didn't precise - assume that SomeClass has fields that - after calling std::move() on its object - would be left invalid (i.e. pointers). – PookyFan Apr 29 '16 at 13:49
  • I am not sure what you mean by pointers "invalid". I think they will be in predictable state (I mean smart pointers of course), so if you check them before dereferencing, you should be fine. – Slava Apr 29 '16 at 13:59
  • @PookyFan, if `queue.push_back` assigns `data` to a temp variable before throwing `bad_alloc` `data` will be invalidated (by your design). So, I'd say this is no guarantee – Super-intelligent Shade Apr 29 '16 at 14:01
  • @Slava When you construct an object using move constructor, the content of the object which's rvalue you're passing to constructor are supposed to get swapped with the content of the object you're constructing right now, no? So if SomeClass has a field of type std::string, after moving it wouldn't be save to access that field in the "source" object anymore, right?. – PookyFan Apr 29 '16 at 14:05
  • @InnocentBystander "if queue.push_back assigns data to a temp variable" - But does it actually happen? Or is it implementation dependent? – PookyFan Apr 29 '16 at 14:06
  • 1
    @PookyFan, I didn't find anything in the standard in this regard, so yes it would be implementation dependent – Super-intelligent Shade Apr 29 '16 at 14:24
  • @PookyFan, maybe add a flag of some sort to `SomeStruct` or set the invalidated pointer to `nullptr` to indicate that it's invalid – Super-intelligent Shade Apr 29 '16 at 14:26
  • @PookyFan wrong. Most standard objects after move left in unspecified but valid state. Some of them in specified state. But all of them valid. If you have your own move ctor or assignment that is different story. Details can be found here http://stackoverflow.com/questions/9168823/reusing-a-moved-container – Slava Apr 29 '16 at 14:30
  • @PookyFan, _"When you construct an object using move constructor, the content of the object which's rvalue you're passing to constructor are supposed to get swapped with the content of the object you're constructing right now, no? "_ No. Not swapped, in general. – Jonathan Wakely Apr 29 '16 at 14:52

2 Answers2

1

It depends why the exception was thrown, but generally speaking, No. The object is in unspecified state after the move operation. Please refer to this question.

The container may and may not use SomeClass's move constructor, but when you are using std::move, you mark the object to be an rvalue reference, you should not be using an object after using it (or passing it around) as rvalue reference.

As a good practice, you should never use std::move if you think about reusing the object.

IIRC, the standard states that objects moved from, remain in unspecified state. That means that you can call member methods and they should work correctly as long as they don't rely on a specific state of the object. Reference needed

Community
  • 1
  • 1
user1708860
  • 1,683
  • 13
  • 32
1

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.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521