Does an exception use move semantics when thrown in C++11?
Asked Answered
P

3

19

http://www.drdobbs.com/cpp/practical-c-error-handling-in-hybrid-env/197003350?pgno=4

In this article Herb Sutter explains that throwing an exception requires a copy of the exception as it's created as a temporary and therefore uses an std::auto_ptr to get round the copy overhead. In light of move semantics being made available in C++11 is this still necessary?

Plenty answered 25/8, 2012 at 13:18 Comment(5)
This copy is routinely optimized away by a decent compiler. You can check yours by setting a debugger breakpoint on the copy constructor.Abjuration
Exception performance shouldn't be your concern. If it is, correct your program so it isn't.Straightjacket
Agreed, this is more academic.Plenty
It does allow throwing move-only types. If it requires copyability, you can't throw a std::unique_ptr for example.Microbiology
At least, MSVC++2013 does not invoke the move constructor for throw std::move(ex);: #32759933Restharrow
V
15

I have just checked, and the Standard allows

  • omitting the copy or move of an object specified by the operand of a throw expression into the exception object
  • omitting the copy or move of the exception object into the catch clause variable of the same type as the exception object if you don't otherwise change the meaning of the program (i.e if you would rethrow and subsequent catches would suddenly see a changed exception object changed by the previous catch block).

Since these omissions are allowed, the spec requires to first regard the source of the copy or move as an rvalue. So this means that the respective objects will be moved if possible. Of course copy and move elision are still allowed as the first choice.


Update

I was notified that the consideration of the exception object initializer of a catch clause parameter as an rvalue initializer will probably be dropped from the Standard (because in general it is not possible for all cases to detect when the behavior of the program is unchanged when omitting a copy/move), so I recommend to not rely on this (second bullet above).

What you can still rely about is the move of a local variable into the exception object, as in throw x; (first bullet above).

Vickyvico answered 25/8, 2012 at 14:25 Comment(2)
When you say "the respective objects will be moved", do you really mean must or may? My apparently duplicate question demonstrates that Visual C++ 2012 does not move the object but invokes the copy constructor instead. Is this a defect according to the standard?Atombomb
@Carsten: I would lean toward that being a bug, per my understanding.Dermatoid
O
2

Move from exception objects is not mandatory now.

It's a defect of C++11. See CWG1493 .

Oleaceous answered 25/4, 2014 at 17:3 Comment(0)
M
0
  • Upon throw expression, a copy of the exception object always needs to be created as the original object goes out of the scope during the stack unwinding process.
  • During that initialization, we may expect copy elision (see this) – omits copy or move constructors (object constructed directly into the storage of the target object).
  • But even though copy elision may or may not be applied you should provide proper copy constructor and/or move constructor which is what C++ standard mandates(see 15.1). See below compilation error for reference.

But modern C++ provides more feature: "Moving safely with move semantics & exception"

struct demo
{
    demo() = default;
    demo(const demo &) { cout << "Copying\n"; }
    // Exception safe move constructor
    demo(demo &&) noexcept { cout << "Moving\n"; }
private:
    std::vector<int>    m_v;
};
int main()
{
    demo obj1;
    if (noexcept(demo(std::declval<demo>()))){  // if moving safe
        demo obj2(std::move(obj1));             // then move it
    }
    else{
        demo obj2(obj1);                        // otherwise copy it
    }
    demo obj3(std::move_if_noexcept(obj1));     // Alternatively you can do this----------------
    return 0;
}
  • We can use noexcept(T(std::declval<T>())) to check if T’s move constructor exists and is noexcept in order to decide if we want to create an instance of T by moving another instance of T (using std::move).
  • Alternatively, we can use std::move_if_noexcept, which uses noexcept operator and casts to either rvalue or lvalue. Such checks are used in std::vector and other containers.
  • This will be useful while you are processing critical data which you don't want to lose. For example, we have critical data received from the server that we do not want to lose it at any cost while processing. In such a case, we should use std::move_if_noexcept which will move ownership of critical data only and only if move constructor is exception-safe.

From: 7 best practices for exception handling in C++

Minyan answered 9/11, 2019 at 8:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.