admin管理员组文章数量:1336576
In the following program, union U
has active member of type A
, which is copied using auto-generated operator =
into another member of type B
, having A
as subobject:
#include <iostream>
struct A { char c[7]; };
struct X { char x; };
struct B : X, A {
using A::operator=;
};
int main() {
union U {
A a{ 0, 1, 2, 3, 4, 5, 6 };
B b;
} u;
//#1
u.b = u.a;
//#2
//u.b = A( u.a );
for ( int i = 0; i < 7; ++i )
std::cout << (int)u.b.c[i];
}
I would like to get 0123456
output, but actual compilers without optimization produce different output.
- MSVC:
0000000
- Clang:
0123355
- GCC and EDG:
0122356
Question #1: Is it because the program has undefined behavior due to simultaneous access to both active and inactive members of the union during copying?
If instead of u.b = u.a;
, I first create a temporary copy, and assign it using u.b = A( u.a );
, then the results of MSVC and Clang become expected 0123456
, but GCC and EDG insist on the same 0122356
. Online demo:
Question #2: Is the program well-formed now, and unexpected result is due to bugs in the implementations?
In the following program, union U
has active member of type A
, which is copied using auto-generated operator =
into another member of type B
, having A
as subobject:
#include <iostream>
struct A { char c[7]; };
struct X { char x; };
struct B : X, A {
using A::operator=;
};
int main() {
union U {
A a{ 0, 1, 2, 3, 4, 5, 6 };
B b;
} u;
//#1
u.b = u.a;
//#2
//u.b = A( u.a );
for ( int i = 0; i < 7; ++i )
std::cout << (int)u.b.c[i];
}
I would like to get 0123456
output, but actual compilers without optimization produce different output.
- MSVC:
0000000
- Clang:
0123355
- GCC and EDG:
0122356
Question #1: Is it because the program has undefined behavior due to simultaneous access to both active and inactive members of the union during copying?
If instead of u.b = u.a;
, I first create a temporary copy, and assign it using u.b = A( u.a );
, then the results of MSVC and Clang become expected 0123456
, but GCC and EDG insist on the same 0122356
. Online demo: https://gcc.godbolt./z/dxfzEM5eM
Question #2: Is the program well-formed now, and unexpected result is due to bugs in the implementations?
Share Improve this question asked Nov 20, 2024 at 10:28 FedorFedor 21.4k37 gold badges55 silver badges168 bronze badges 7 | Show 2 more comments1 Answer
Reset to default 3In a union, at most one member can be active (within lifetime) at any given moment ([class.union.general]/2).
Evaluating u.b = u.a
implicitly starts the lifetime of the b
member, thus ending the lifetime of u.a
([basic.life]/2.5). According to [class.union.general]/5,
... no initialization is performed [for the created object] and the beginning of its lifetime is sequenced after the value computation of the left and right operands and before the assignment.
By the time the assignment takes place, u.a
is no longer within lifetime, thus reading from it has undefined behavior.
In contrast, in u.b = A( u.a )
the initialization of the temporary A
object happens while u.a
is still active, which avoids the lifetime issue and makes the assignment well-defined.
Worth noting that the above wording first appears only in C++17 (originating from P0137). The interaction of unions with object lifetime was largely unspecified prior to that, so there might not be a definitive answer to this question based on the normative text of C++11 as published. However, an example in [class.union] seems to suggest that the implicit change of active union member by assignment was at least intended to be valid back then as well.
本文标签: cCopying overlapping objects with autogenerated operator Stack Overflow
版权声明:本文标题:c++ - Copying overlapping objects with auto-generated operator = - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742365182a2461148.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
E1 @= E2
is guaranteed since C++17,E2
happens beforeE1
. For previous version, there are no guarantee order, so potentially UB: evaluateu.b
before ends lifetime ofu.a
. – Jarod42 Commented Nov 20, 2024 at 10:33A a = u.a; u.b = u.a;
should produce expected result. – Jarod42 Commented Nov 20, 2024 at 10:38constexpr
allows to detect some UBs, and indeed, clang and msvc report an issue Demo – Jarod42 Commented Nov 20, 2024 at 10:43char x
with some stateless spacers to get a similar behavior, but avoid uninitialized data in the constexpr branch. If you add/remove the cast, add/remove constexpr on the foo function, and add/remove constexpr on the result in main, you get: clang thinks (A) cast in assignment is needed for it to be constexpr, gcc doesn't, and gcc writes the "correct" code in a constexpr case and "bad" code in the non-constexpr case. The (A) cast doesn't change gcc's "bad" code. – Yakk - Adam Nevraumont Commented Nov 20, 2024 at 18:15constexpr
version of this program. And MSVC error was due to return fromconstexpr
functionB
with uninitializedx
field. After returning only copied part, MSVC is fine with the code – Fedor Commented Nov 20, 2024 at 20:17