https://quuxplusone.github.io/blog/2022/07/09/lifetime-extended-assignments/ [pumpkin-to] Arthur O'Dwyer Stuff mostly about C++ Recent Date Tag About Fun with lifetime-extended results of assignment Previously on this blog: "Fun with conversion-operator lookup" (2021-01-13). Here's another simple C++ program that gives different output on three of the four mainstream compilers (Godbolt): #include char messages[][23] = { "This is Clang (or EDG)", "This is GCC", "This is MSVC", }; int count; struct X { ~X() { ++count; } }; int main() { { X& a = (X() = X()); count = 0; } puts(messages[count]); } As far as I can tell, Clang and EDG are correct; the other two are buggy. --------------------------------------------------------------------- We create two X objects in this program: the temporary on the left-hand side of the assignment operator, and the temporary on the right-hand side. MSVC seems to be doing a sort of "lifetime extension" of both objects all the way to the end of a's scope. [class.temporary]/6 doesn't seem to offer any textual justification for extending the left-hand operand, let alone the right-hand operand. GCC seems to be extending the lifetime of the left-hand X() but not the right-hand one. (We can tell by instrumenting the constructors: Godbolt.) But here's the weird part: GCC does this lifetime extension only when struct X has no named fields! That is, GCC will lifetime-extend the left-hand operand of an assignment when its type is any of these: struct alignas(4) X { ~X(); }; struct X { int :0; ~X(); }; struct X { int :16; ~X(); }; but not when its type is either of these: struct X { int i:1; ~X(); }; struct X { char c; ~X(); }; Incidentally, MSVC is the only vendor that allows struct X { int :16; } to qualify as std::is_empty. I think MSVC is correct (and Clang/GCC are wrong): such an X certainly has no non-static data members, since unnamed bit-fields are not members at all. Finally, all this wackiness manifests only when X::operator= is defaulted (either implicitly, as shown here, or explicitly via = default). If you provide your own user-defined X& operator=(X&&) { return *this; } then the divergence vanishes: all vendors agree that there shouldn't be any lifetime extension in that case. Posted 2022-07-09 implementation-divergence lifetime-extension