Open xmh0511 opened 2 years ago
It doesn't make sense that we identify or create the corresponding object o for each object nested within the object. Since [basic.life] p1 says such an initialization will begin the lifetime of that object.
[basic.life]/1:
except that if the object is a union member subobject or
subobject thereofnested within itSo, is the following phrase the intent of [class.copy.ctor] p15?
The intent is more like
For each object oS nested within ([intro.object]) the object that is the source of the copy, a corresponding object oD nested within the destination is identified (if
the objectoS is a subobject) or created (otherwise), and, if oS is within its lifetime, the lifetime of oD begins before the copy is performed.
except that we may not want to start the lifetime of objects of a non-implicit-lifetime type (and everything nested within them), e.g. a vector
stored in a union member subobject:
union U { unsigned char buf[9000]; } u {};
auto* pv = new (&u.buf) std::vector<int> {};
U u2 = u; // do we want to start the lifetime of the object corresponding to *pv? Maybe not even create it?
@zygoloid
@languagelawyer Seems right, I think the last sentence is more accurate if it is that
the lifetime of oD begins before the copy from
Os
is performed.
Consider this example
union U{
char c = 0;
int a;
} u;
There are two objects nested within the object u
, how will the copy perform? Firstly copy c
, then copy a
? Or, we just copy c
since it is active?
There are two objects nested within the object
u
, how will the copy perform? Firstly copyc
, then copya
?
There is no first or second, the whole object representation of u
is copied:
The implicitly-defined copy/move constructor for a union X copies the object representation ([basic.types]) of X.
@languagelawyer if oS is a subobject
, Does this subobject intend to refer to indirect subobject?
union U{
struct C{
int i;
struct X{} x;
} c = {0};
char c2;
};
U u1;
U u2 = u1;
U::c::i and U::c::x are all subobjects of U::c, which in turn is a subobject of u1, they all within their lifetime. So, U::c::i
, U::c::x
and U::c
in u2
will be identified and their lifetime will begin before the copy.
Does this subobject intend to refer to indirect subobject?
There is no such thing as direct or indirect subobject. NSDM is what can be direct or indirect. Subobject is just subobject. It means an object which corresponds to a NSDM, a base class, or which is an array element.
Does this subobject intend to refer to indirect subobject?
There is no such thing as direct or indirect subobject. NSDM is what can be direct or indirect. Subobject is just subobject. It means an object which corresponds to a NSDM, a base class, or which is an array element.
I meant the subobjects corresponding to indirect NSDMs or Base classes. Do these subobject conform to be os
?
I meant the subobjects corresponding to indirect NSDMs or Base classes. Do these subobject conform to be os?
Yes, why not.
@languagelawyer Alright, I think your proposal looks good to me.
@languagelawyer Hmmm... I still think we should completely say what these subobjects os
can be. Since, for a non-union class, the members of the base classes are also members of the derived class, hence, we can say the subobject of these members are subobjects of the object of the derived class, as per [intro.object] p2
A subobject can be a member subobject ([class.mem]), a base class subobject ([class.derived]), or an array element.
However, this is different in a union class. Firstly, a union class cannot have any base class. There is also no provision that says that: the subobject of an object corresponding to the NSDM of a union is also a subobject of that union.
union U{
struct X{ int a;} x;
} u;
x
is a subobject of u
, X::a
is a subobject of x
, however, it never says that X::a
is a subobject of u
. So, should we change if oS is a subobject
to if oS is a subobject or a subobject thereof
?
Since, for a non-union class, the members of the base classes are also members of the derived class
It means members, not member subobjects.
hence, we can say the subobject of these members are subobjects of the object of the derived class, as per [intro.object] p2
I don't see the connection between the first and the second part.
There is no provision that says that the subobject of an object corresponding to the NSDM of a union is also a subobject of that union.
And we don't want such provision. There is «nested within» relation for such transitivity.
So, should we change
if oS is a subobject
toif oS is a subobject or a subobject thereof
?
We don't care whose subobject oS is, only is that it is a subobject.
For the first opinion, since a subobject corresponding to a member is a member subobject, hence any subobject corresponding to a direct/indirect member is also a member subobject of the containing object.
Do we indeed not care whether the object os is a subobject of the source object in the copy? Consider this example
struct C{
int a = 0;
};
union U{
char buff[128];
};
U u = {0}; // start the lifetime of buff
new (u.buff + 4) C;
U u2 = u;
Although, the created object by the new-expression is not a subobject of buff
or u
, however, the object C::a
is a subobject of the object of type C
. Is C::a
the os
here? It is similar to the example in your first comment. Presumably, C::a
is not os
. Maybe, we just want the subobject of u
or the subobject ... of one subobject of u
to be os
?
For the first opinion, since a subobject corresponding to a member is a member subobject, hence any subobject corresponding to a direct/indirect member is also a member subobject of the containing object.
We want the «nested within» relation to be a tree, not a DAG, so an object corresponding to a base class (direct) NSDM is not a subobject of an object of derived type. (However, see #2310)
Do we indeed not care whether the object os is a subobject of the source object in the copy?
We do not care.
Is C::a the os here?
Check the definition of https://timsong-cpp.github.io/cppwp/n4868/intro.object#def:nested_within
@languagelawyer I think I have seen your point. Did you mean
For each object oS nested within ([intro.object]) the object O that is the source of the copy, a corresponding object oD nested within the destination is:
- if the object oS is a subobject of O, identified, or
- if the object oS is nested within O but not a subobject of O, created
in either case, if oS is within its lifetime, the lifetime of oD begins before the copy is performed.
This may introduce an issue that if Os
has a non-trivially copyable type. If we say we begin the lifetime of oD
, that means we can manipulate oD
in any way as per [basic.life] after copying from oS
?
Did you mean … For each object oS nested within ([intro.object]) the object O that is the source of the copy … if the object oS is a subobject of O … if the object oS is nested within O but not a subobject of O
I think I wrote this several times: oS is just a subobject. Without "of".
@languagelawyer Sorry. Maybe, I still don't understand your point.
struct Vector{
int buff[2];
};
union U{
struct C{
char buff[1000];
int i;
struct X{} x;
} c = {0};
char c2;
};
U u;
auto ptr = new(u.c.buff +4) Vector{};
I wonder which objects in u
can be called oS
?
I wonder which objects in u can be called oS?
Every object which is nested within u
I wonder which objects in u can be called oS?
Every object which is nested within
u
@languagelawyer So, oS
can be every object nested within the u
, Right? Then, in a destination
, which objects will be identified? which objects will be created? Moreover, which objects will be begun their lifetime in the destination?
Then, in a
destination
, which objects will be identified? which objects will be created? Moreover, which objects will be begun their lifetime in the destination?
See https://github.com/cplusplus/draft/issues/5193#issuecomment-1008861006
See #5193 (comment).
So, the conclusion to the above example in that comment is that:
Subobjects are:
the subobjects of
u
- u.c2
- u.c
the subobjects of
u.c
- u.c. buff
- u.c.i
- u.c.x
the subobjects of each element of
u.c. buff
the subobjects of the object referred to by
*ptr
- (*ptr). buff.
the subobjects of each element of
(*ptr).buff
which are all so-called subobjects nested within
u
and are identifiedInstead, the object that is created is:
- the object referred to by
*ptr
which is nested in
u
but itself is not a subobject.
Is this also your opinion?
except that we may not want to start the lifetime of objects of a non-implicit-lifetime type (and everything nested within them), e.g. a vector stored in a union member subobject:
Perhaps we also need to allow other types for implicitly-defined assignment operators. Consider the following example:
struct Weird {
// no default ctor
Weird(int) {}
Weird(const Weird&) {} // non-trivial
Weird(Weird&&) {} // non-trivial
Weird& operator=(const Weird&) = default;
Weird& operator=(Weird&&) = default;
};
union Foo { // has deleted default/copy/move ctor, and eligible trivial copy/move assignment
char dummy;
Weird w;
};
int main()
{
Foo x{.w{42}}, y{.dummy{}};
y = x; // it seems that y.w should be made active
}
In this case, Weird
is neither implicit-lifetime nor trivially copyable, but it makes sense that y = x
makes y.w
active.
@frederick-vs-ja Sure, I have said that
The similar issue is also in [class.copy.assign]p13.
So, [class.copy.assign]p13 is reasonable to be changed if [class.copy.ctor] p15 has revisioned.
it makes sense that
y = x
makesy.w
active.
When y.w = x.w
doesn't? I'm not sure.
Anyway, this union shenanigans need a holistic review to decide how it interact with each other and the rest of the object model.
[class.copy.ctor] p15 says
However, [class.union.general] p2 says
It doesn't make sense that we identify or create the corresponding object
o
for each object nested within the object. Since [basic.life] p1 says such an initialization will begin the lifetime of that object. If we copy each object into the destination, it will make no sense. Consider this exampleI think the copy initialization at
#1
just needs to create the object that corresponds toU::i
intou2
and copy the data fromu.i
. So, is the following phrase the intent of [class.copy.ctor] p15?The similar issue is also in [class.copy.assign]p13.