New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[basic.life] Does storage reuse update pointers/references/names when the original object’s lifetime has not ended? #4906
Comments
Note the "before" and "after"
It means the subclause is intended to cover concurrent cases. We should clearly phrase what the state of the storage is at that point time. In short, only if there are no other objects reuse the storage at that time and the lifetime of the original object occupied that storage has ended, the newly created object at that time can be granted to have these properties. |
Alright for the condition ‘no other storage reuse’ covering concurrency. But for the condition ‘original object’s lifetime has ended’, why is it necessary? |
Because we want to stress the concept automatically refers to the new object. A pointer may be obtained during the lifetime of the original object, the pointer hence points to that object through the lifetime of that object. Once the lifetime of that object has ended, the pointer is still considered to point to that object that has been expired. Hence, in this situation, a new object created at the storage that satisfies certain conditions can make the pointer/reference be refreshed to refer to the new object. To end the lifetime of an object, it is either the object is destroyed or its destructor is called, or the storage the object occupies is reused. Since it is associated with concurrent, and we should clearly expound the state as the above said, the original object’s lifetime has ended is necessary. |
… or released.
So I still don’t get why original object’s lifetime ended is part of the initial state in the standard. |
Assume the initial wording was "if a new object reuses the storage occupied by an original object", if there are multi threads, in each thread there is a new object that will create at that storage location. Which is the new object and the original object we are saying? In other words, is there more simple way to phrase the between-situation? or stress that this rule only applies to a particular object at some time. |
How is it possible since we assumed ‘before the storage which the object occupied is reused’? |
@xmh0511 You seem to concur with me that the condition ‘whose lifetime has ended’ is unnecessary. |
I still retain my opinions that differ from yours. I don't know what way will you rephrase that rule with your wording. Maybe, you should leave your proposal here, and further analysis could be done according to that proposal. |
Could I ask for a specific wording suggestion to be considered? (I don't think an outright defect has been identified here.) |
I would just remove the end of lifetime requirement which seems to me unnecessary, that is I would transform
into
|
Consider a situation, if a value computation of the original object by a name/pointer/reference and the application of [basic.life/8] you have modified have an exact overlap(i.e happen at the same time, especially in a multi-thread scene), which is the object the value computation performs on? |
Here is the timeline with the requirement that the original object’s lifetime has ended (the current standard):
Here is the timeline without the requirement that the original object’s lifetime has ended (my proposal):
I think it is independent of the presence or absence of the GAP above. If you retrieve a value during the original object lifetime, then you get its value. If you retrieve a value during the new object lifetime, then you get its value. If you retrieve a value during the GAP (if any, cf. my proposal) or during the new object initialization, then you get an indeterminate value. |
If we have a "GAP" situation, any value computation or side effect that performs during this situation has explicitly undefined behavior. "original object’s lifetime has ended" has this effect that we cannot use the name/reference/pointer to manipulate the object until the lifetime of a new object has started, which could arguably be called a bound. If we do not give that "GAP", as I said above, any value computation or side effect by using the "name/pointer/reference" should have been well-formed but the overlap action(create a new object) would make that context vague(break the well-formed operation). |
@jensmaurer The issue is that the paragraphs says «If, after the lifetime of an object has ended», but we want it to apply in case when we reuse the storage of an object whose lifetime can't be ended¹, which happens to union U
{
int i;
float f;
} u; // don't remember if this makes U::i active, U::f is not active for sure
u.f = 0; // to make this defined behavior
// [class.union] creates a new object of float type
// however, since u.f denoted an object which has never been alive
// the creation of the new object doesn't end the old one's lifetime
// and the paragraph does not "rebind" U::f to the new object 1 This relies on the following observation: Note the present tense. Since a dead object (which is also not during its periods) does not occupy the storage, its lifetime can not ended by its former/potential storage reuse. |
union U
{
int i;
float f;
} u; // don't remember if this makes U::i active, U::f is not active for sure
It seems that neither of the members is active, did you mean union U
{
int i;
float f;
} u{}; // as per [dcl.init.aggr#5.5] and [dcl.init.list#3.11], the first variant member is initialized and active. It is indeed an issue that, since |
I needed
If this conclusion comes from that the Standard doesn't say how/when the corresponding subobject appear, then this is not unique for unions. For So I think the implied model is that when an object, whether of union or non-union class type, is created, it has subobjects corresponding to each NSDM, even though, in the union case, at most one of them is initialized and brought to life. |
If the concern wasn't arisen from "whether storage occupied by a subobject corresponding to a member even though it was not be constructed", I didn't see the issue in [basic.life] p8
In this provision, it uses "occupied" for object Back to this example union U
{
int i;
float f;
} u{}; the object associated with |
Also, the sentence seems to have a logical paradox. [basic.life] p1 says
Either requirement that is satisfied will end the lifetime of the object. The third bullet causes the paradox here. [basic.life] p8 says
Note that the storage reuse can end the object's lifetime. In the formal example, there is no problem this->~C(); // explicitly invoke the destructor ends the lifetime
new (this) C(other); // the evaluation of the creating new object happens after the lifetime is ended However, consider this case: if we do not explicitly call the destructor, instead, by directly creating the new object, which will reuse the storage new (this) C(other); // reuse storage ends the lifetime The storage reuse results in the lifetime being ended. So, which one is counting to happen before the other? In common sense, the reuse action should occur first such that the lifetime of the original object will be ended due to the reuse. That is to say [basic.life] p8 seems not to be suitable for the second case? In addition, the evaluation of
|
[basic.life/8] specifies (bold emphasis of the condition mine):
Basically the condition is this: if a new object reuses the storage occupied by an original object whose lifetime has ended, then…
But why isn’t it just this (i.e. removing the condition ‘whose lifetime has ended’): if a new object reuses the storage occupied by an original object, then…
In other words, does the consequence on pointers/references/names referring to the original object still apply with that less strict condition?
The text was updated successfully, but these errors were encountered: