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.
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).
A move constructor enables the resources owned by an rvalue object to be moved into an lvalue without copying.
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.
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.
§ 15.1 [except.throw]:
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.
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]:
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]:
- 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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With