Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using the move constructor to throw exceptions? (C++)

If I have an object e of type Error which implements a move constructor, will throwing std::move( e ) use the move constructor of Error to "duplicate" e, so does it avoid making an actual copy of the object? So if I have

Error e;

throw std::move( e );

will the copy constructor of Error be called or not? This is of interest when your move constructor is noexcept (as it should be), but your copy constructor isn't.

like image 648
sperber Avatar asked Aug 27 '14 18:08

sperber


People also ask

Can STD move throw?

Yes, throwing move constructors exist in the wild. Consider std::pair<T, U> where T is noexcept-movable, and U is only copyable (assume that copies can throw).

What does the move constructor do?

A move constructor enables the resources owned by an rvalue object to be moved into an lvalue without copying.

What happens when you throw an exception from the constructor?

When throwing an exception in a constructor, the memory for the object itself has already been allocated by the time the constructor is called. So, the compiler will automatically deallocate the memory occupied by the object after the exception is thrown.

Can a copy constructor throw an exception?

However, the copy constructor for an exception object still must not throw an exception because compilers are not required to elide the copy constructor call in all situations, and common implementations of std::exception_ptr will call a copy constructor even if it can be elided from a throw expression.


1 Answers

§ 15.1 [except.throw]:

  1. Throwing an exception copy-initializes (8.5, 12.8) a temporary object, called the exception object. The temporary is an lvalue and is used to initialize the variable named in the matching handler.

  2. When the thrown object is a class object, the constructor selected for the copy-initialization and the destructor shall be accessible, even if the copy/move operation is elided (12.8).

§ 8.5 [dcl.init]:

  1. The initialization that occurs in the form

    T x = a;

, as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.5.1) is called copy-initialization. [ Note: Copy-initialization may invoke a move (12.8). —end note ]

§ 12.8 [class.copy]:

  1. When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.

The aforementioned criteria for copy-elision include the following (§12.8 [class.copy]/p31):

  • in a throw-expression, when the operand is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one), the copy/move operation from the operand to the exception object (15.1) can be omitted by constructing the automatic object directly into the exception object

Copy-initialization of an exception may invoke a move-constructor to construct the actual exception object (even if std::move(e) is not explicitly invoked in a throw excpression), but not its matching handler (if tried to be caught by value).

like image 161
Piotr Skotnicki Avatar answered Oct 24 '22 01:10

Piotr Skotnicki