Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Move semantics and Exceptions

People also ask

What is a move semantic?

Move semantics is a set of semantic rules and tools of the C++ language. It was designed to move objects, whose lifetime expires, instead of copying them. The data is transferred from one object to another. In most cases, the data transfer does not move this data physically in memory.

When should I use move semantics?

Move semantics allows you to avoid unnecessary copies when working with temporary objects that are about to evaporate, and whose resources can safely be taken from that temporary object and used by another.

How are Move semantics implemented?

Move semantics aim to avoid the copying of data from temporary objects by instead stealing the memory location of where the object resides. This behaviour is implemented through the use of a move constructor and move assignment operator that act only on rvalue references.

What is the point of STD move?

std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object. In particular, std::move produces an xvalue expression that identifies its argument t . It is exactly equivalent to a static_cast to an rvalue reference type.


I believe that the standards committee originally attempted to make it so move constructors would not be allowed to throw exceptions, but (at least as of today) found that trying to enforce that had too many pitfalls.

Proposal N3050, "Allowing Move Constructors to Throw (Rev 1)", has been incorporated into the draft standard. Essentially the proposal adds the capability for move constructors to throw, but to disallow 'throwing' moves to be used for certain operations where strong exception safety guarantees were needed (the library will fall back to copying the object if a non-throwing move isn't available).

If you mark a move constructor as non-throwing (noexcept) and an exception is thrown, std::terminate() will be called.

  • http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3050.html

It might also be worth reading a blog article by David Abrahams that discusses the issues that N3050 was intended to address:

  • http://cpp-next.com/archive/2009/10/exceptionally-moving/

Depends on the type being moved from. It's possible to explicitly throw an exception, of course, from a move ctor, but also to implicitly call a copy ctor for a subobject from a move ctor. That copy ctor might do something that may throw, such as allocate memory. So, for the source object, the minimum guarantee is the original value may or may not remain, but it should still be in a destructable state.

For the object being moved to, it's the same as throwing from a ctor in current C++: destroy any constructed bases and members, execute the ctor's function try handler, if any, then propagate the exception. Details are in N3225 §15.2p2.

In particular, note that containers require their allocator types not have throwing move ctors:

Such move construction of the allocator shall not exit via an exception. [N3225 §23.2p8]

This allows the containers to move allocators around and use those allocators to clean up their items in the case of an exception when moving or copying items.


Your question then amounts to a question about Exception Guarantees. There are 3 types of Exceptions Guarantees (that apply to functions):

  • No exception guarantee at all (not really a type... but it may happen if no concern is given to the matter)
  • Basic Exception Guarantee: Technically correct, but not Functionally correct (ie no resource is leaked, the program will terminate without an abrupt halt, but it may have undesired side-effects, like the payment being cashed-in but the command not being registered)
  • Strong Exception Guarantee: All or Nothing (like a transaction), that is either everything is done right, or we rollback to the previous state.
  • No Throw Exception Guarantee: This does not throw, ever, so no worry.

When you compose your function, you usually pick up existing functions with their own guarantees. It is difficult to increase the exception guarantee, that is your are generally limited by the weakest guarantee used.

W.r.t your question, it takes at least a Strong Exception Guarantee for the original object to be left untouched if an exception is thrown.

So, what happens if an exception is thrown during move-construction ? It depends on the guarantees exhibited by the sub-objects and the way you combined the calls...

  1. If an exception is thrown from a constructor, the object is not being built and all built subobjects are destroyed, in reverse order. This rule is also applicable to a move-constructor
  2. Unless you "wrap" the constructor in a try catch and somehow restore the objects that have been moved, they'll have lost their resources. Note that they must still be in a destructable state anyway, so technically the program will be correct.

In terms of exceptions guarantees, it means that by default, if all of the subobjects' constructors at least meet the Basic Exception Guarantee, then your move constructor will too, without any special care.

However, even if all the subobjects' constructors meet the Strong Exception Guarantee, it's unlikely you'll succeed in having your own move constructor meet it: this is the same issue that chaining transactions does not yield a transaction.

If only one of the subobjects' constructors may throw, and it meets the Strong Exception Guarantee, then your move constructor will naturally meet it itself if you initialize the throwing object first.

Hope this helped... exceptions are a wild beast to tame :)