Fun with lifetime-extended results of assignment
source link: https://quuxplusone.github.io/blog/2022/07/09/lifetime-extended-assignments/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
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 <stdio.h>
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 asstd::is_empty
. I think MSVC is correct (and Clang/GCC are wrong): such anX
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.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK