diff --git a/c++/nda/mem/handle.hpp b/c++/nda/mem/handle.hpp index bf3ed597c..084acdab0 100644 --- a/c++/nda/mem/handle.hpp +++ b/c++/nda/mem/handle.hpp @@ -169,9 +169,11 @@ namespace nda::mem { * @brief Move assignment operator first releases the resources held by the current handle and then moves the * resources from the source to the current handle. * - * @param h Source handle. + * @param h Source handle. Must not alias `*this`. */ handle_heap &operator=(handle_heap &&h) noexcept { + EXPECTS(this != &h); + // release current resources if they are not shared and not null if (not sptr and not(is_null())) destruct({_data, _size}); @@ -206,6 +208,7 @@ namespace nda::mem { * @param h Source handle. */ handle_heap &operator=(handle_heap const &h) { + if (this == &h) return *this; *this = handle_heap{h}; return *this; } @@ -382,9 +385,10 @@ namespace nda::mem { /** * @brief Move assignment operator simply calls the copy assignment operator. * @details If an exception occurs in the constructor of `T`, the program terminates. - * @param h Source handle. + * @param h Source handle. Must not alias `*this`. */ handle_stack &operator=(handle_stack &&h) noexcept { + EXPECTS(this != &h); operator=(h); return *this; } @@ -401,6 +405,7 @@ namespace nda::mem { * @param h Source handle. */ handle_stack &operator=(handle_stack const &h) { + if (this == &h) return *this; for (size_t i = 0; i < Size; ++i) new (data() + i) T(h[i]); return *this; } @@ -545,9 +550,10 @@ namespace nda::mem { * * @details In both cases, it resets the source handle to a null state. * - * @param h Source handle. + * @param h Source handle. Must not alias `*this`. */ handle_sso &operator=(handle_sso &&h) noexcept { + EXPECTS(this != &h); clean(); _size = h._size; if (on_heap()) { diff --git a/test/c++/nda_mem.cpp b/test/c++/nda_mem.cpp index 9013deedf..35f12a8ca 100644 --- a/test/c++/nda_mem.cpp +++ b/test/c++/nda_mem.cpp @@ -121,18 +121,21 @@ H check_handle() { move2 = std::move(copy1); EXPECT_EQ(handle.size(), move1.size()); EXPECT_EQ(handle.size(), move2.size()); - std::swap(handle, handle); // check self swap +#ifdef NDEBUG // self-swap violates the handle move-assign precondition; release-only. + std::swap(handle, handle); +#endif for (int i = 0; i < handle.size(); ++i) { EXPECT_EQ(handle[i], static_cast(i)); EXPECT_EQ(move1[i], static_cast(i)); EXPECT_EQ(move2[i], static_cast(i)); } - // check self move assignment (see https://stackoverflow.com/questions/9322174/move-assignment-operator-and-if-this-rhs) +#ifdef NDEBUG // self-move-assign violates the handle move-assign precondition; release-only. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wself-move" move1 = std::move(move1); #pragma GCC diagnostic pop +#endif return handle; }