Assume the following c++17 code:
#include <type_traits>
namespace dtl
{
struct One
{
explicit One(int);
~One() = default;
One(const One &) = delete;
auto operator=(const One &) -> One & = delete;
auto operator=(One &&) -> One & = delete;
One(One &&); // Throwable, not default;
int m_int;
};
struct Two
{
explicit Two(int);
~Two() = default;
Two(const Two &) = delete;
auto operator=(const Two &) -> Two & = delete;
auto operator=(Two &&) noexcept -> Two & = delete;
Two(Two &&) noexcept = default;
One m_one;
};
One::One(One &&) { throw 1; }
static_assert(std::is_nothrow_move_constructible_v<Two>);
}
Here we clearly see that the move constructor of the class One
ain't marked as noexcept. Class Two
has a defaulted move constructor that is explicitly requested to be noexcept.
If we check this code, this compiles with GCC trunk, Clang trunk, MSVC 19.28 and fails with MSVC19.24.
I checked the following sources which seem to tell me that the move constructor of Two
needs to be deleted:
- Program with "noexcept" constructor accepted by gcc, rejected by clang
- 'noexcept = default' compilation error
- Bug 35204 - std::chrono exception specification of explicitly defaulted default constructor does not match the calculated one
CWG issue 1778 to read (N4296 [dcl.fct.def.default]/p3):
If a function that is explicitly defaulted is declared with an exception-specification that is not compatible (15.4) with the exception specification on the implicit declaration, then
if the function is explicitly defaulted on its first declaration, it is defined as deleted; otherwise, the program is ill-formed.
Based on that information, I can only conclude that all 3 compilers are wrong in considering Two
as no_throw_move_constructible and the move constructor should be implicitly deleted. As it's strange for all 3 to ignore the standard on this, I do wonder: Is this really a compiler bug or am I missing something.