https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102221
--- Comment #5 from Giuseppe D'Angelo <dangelog at gmail dot com> --- Here's a further testcase that doesn't even use unique_ptr: #include <utility> #include <memory> using ptr = int *; using rawptr = int *; #ifndef RESTRICT #define RESTRICT #endif void swap(ptr & RESTRICT a, ptr & RESTRICT b) { if (std::addressof(a) == std::addressof(b)) __builtin_unreachable(); ptr tmp = a; a = nullptr; // ptr tmp = std::move(a) // a = std::move(b) #if ONE { // a.reset(b.release()) rawptr tmp = b; // b.release() b = nullptr; rawptr old = a; // a.reset(tmp) a = tmp; delete old; } #elif TWO { // move+swap rawptr tmp = b; b = nullptr; // ptr tmp = std::move(b) { // a.swap(tmp) rawptr tmp2 = a; a = tmp; tmp = tmp2; } // ~tmp delete tmp; } #elif THREE { delete a; a = b; b = nullptr; } #else #error #endif delete b; b = tmp; tmp = nullptr; // b = std::move(tmp) delete tmp; } https://gcc.godbolt.org/z/bv1T64ndo ONE and TWO don't elide the delete, unless the arguments are marked __restrict__. THREE does elide it. Sounds like some escape analysis going wrong, combined with unique_ptr's self-move-assignment safety (that "bans" THREE's implementation).