C++ – Rules for noexcept on Default Defined Move Constructors

c++c++11move-semanticsnoexceptpimpl-idiom

Especially in connection with std::vector it is important that types are noexcept movable when possible.

So when declaring a move constructor = default like in

struct Object1
{
    Object1(Object1 &&other) = default;
};

std::is_nothrow_move_constructible<Object1>::value will be true as every member (0 here) of Object1 is nothrow-move-constructible, which is answered here.

Yet what happens if the move copy constructor is only declared and then later = default defined like in the following code?

struct Object2
{
    Object2(Object2 &&other);
};
Object2::Object2(Object2 &&other) = default;

With g++ 4.9.2 std::is_nothrow_move_constructible<Object2>::value is false and I have to mark both the declaration and the definition as noexcept to make it true.

Now what I am interested in is what the actual rules are.
Especially since Item 22 in Effective Modern C++ (Scott Meyers) seems to give ill advice by suggesting to implement the pimpl-idiom move constructor like I did with Object2.

Best Answer

[dcl.fct.def.default]/p2:

If a function is explicitly defaulted on its first declaration,

  • it is implicitly considered to be constexpr if the implicit declaration would be, and,
  • it has the same exception specification as if it had been implicitly declared (15.4).

These rules do not apply if the function is explicitly defaulted on a later declaration, as in your later example, so instead, except for destructors, the function is considered noexcept(false) by default like most other functions.

Since the explicit defaulting can be in a different translation unit - and in the pimpl case, is in a different TU - there's no general way for the compiler to figure out after seeing the class definition only whether the move constructor will throw, unless the function is explicitly defaulted in the class definition (i.e., at its first declaration).

Related Question