Consider the following example:
#include <iostream>
struct X
{
X() = default;
X(const X&) = default;
X(X&&) = default;
X& operator = (const X&) = default;
X& operator = (X&&) = default;
};
int main()
{
static_assert(std::is_nothrow_move_assignable_v<X>);
return 0;
}
The move assignment operator appears to be implicitly noexcept, since the static assertion passes.
However, defining it out-of-line makes the assertion fail:
#include <iostream>
struct X
{
X() = default;
X(const X&) = default;
X(X&&) = default;
X& operator = (const X&) = default;
X& operator = (X&&); //= default;
};
X& X::operator=(X&&) = default;
int main()
{
static_assert(std::is_nothrow_move_assignable_v<X>);
return 0;
}
Why do they behave differently?
In your second code snippet, the move-assignment function is actually user-provided, because you have (explicitly) declared it – but not defaulted it – in its first declaration.
From this Draft C++17 Standard (bold emphasis mine):
To get the same behaviour (i.e., to make that 'defaulted' function
noexcept) as in the first snippet, but without specifying the= defaultin the declaration, you will need to explicitly add thenoexceptattribute, both to the declaration and to the later definition: