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):
11.4.2 Explicitly-defaulted functions [dcl.fct.def.default]
…
5 … A function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration. A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted; if such a function is implicitly defined as deleted, the program is ill-formed.
To get the same behaviour (i.e., to make that 'defaulted' function noexcept
) as in the first snippet, but without specifying the = default
in the declaration, you will need to explicitly add the noexcept
attribute, both to the declaration and to the later definition:
struct X
{
X() = default;
X(const X&) = default;
X(X&&) = default;
X& operator = (const X&) = default;
X& operator = (X&&) noexcept; // Add "noxcept" here ...
};
X& X::operator=(X&&) noexcept = default; // ... AND here!
int main()
{
static_assert(std::is_nothrow_move_assignable_v<X>);
return 0;
}