Date: Mon, 2 Oct 2023 09:57:58 -0400
On Mon, Oct 2, 2023 at 6:51 AM Mykola Garkusha via Std-Discussion
<std-discussion_at_[hidden]> wrote:
> Trying to make sense of the [basic.lifetime] section of the standard, but it
> appears to be contradictive in a few places.
>
> While I'm generally I'm aware that there is work still going on to further
> improve the standard, there is one particular contradiction that I haven't
> seen covered. http://eel.is/c++draft/basic#life-8 places restrictions in case
> the object lifetime ended but *before* the storage has been *released* or
> *re-used*. While mentioning *released* makes sense, *re-used*, in my opinion,
> brings contradiction since most often the new object would be created using
> placement new, and now the latest standard does clearly mention that storage
> is first released in placement new, and then the new object is created
> [http://eel.is/c++draft/basic#life-1.5]
A nitpick: storage is not *released* by a placement new-expression, it is only
*reused* when the new-expression obtains storage for the new object.
> Wouldn't the drop of *re-used* from http://eel.is/c++draft/basic#life-8 make
> it non-contradictive? My reasoning is that as *re-used* is defined at least
> for placement new (maybe memcpy as well?) the following code from the example
> in [basic.life]#8
>
> this->~C(); // lifetime of *this ends
> new (this) C(other); // new object of type C created. But why storage is not considered re-used given new rules in [basic.life]#1 around placement new
>
> would imply the following sequence of ordered events
> (1) end of a lifetime for the object which this used to point to ->
> (2) storage which this used to to point is *re-used* [basic.life]#1 ->
> (3) new object C is created at the storage which is used to point to
>
> Then if the above is correct, the condition is in [basic.life]#8 cannot apply
> to the corresponding example as (1) happens before (2) and (2) happens before
> (3). Now if (2) would have been dropped as the required condition then it
> would all make sense. However looks like from the comment in the example
> above, that the standard omits *re-use* in "new (this) C(other"
>
> Any thoughts on this? Or I've missed some important piece in my reasoning?
>
> Kind Regards,
> Mykola
Simply removing "reused" from [basic.life]/8 would be overly broad: if the
storage of a T object is reused for a U object, and then the storage of the U
object is reused for another T object, then I don't think it's intended for the
first T object to be transparently replaceable by the second. It would also have
to say that only the first object to reuse the storage is eligible for
transparently replacing the original object.
Otherwise, I agree that the wording is a bit weird. I think a lot of it comes
down to when exactly the new object is *created*, which is the precise operation
that [basic.life]/8 cares about. One possible interpretation can be derived from
the wording of [intro.object]/10, where implicitly creating objects "creates and
starts the lifetime" of those objects. If "starting the lifetime" of an object
includes obtaining its storage per [basic.life]/(1.1), then this implies that
creating an object occurs *before* obtaining its storage. Thus, under this
reading, there is no contradiction in [basic.life]/8, since the storage hasn't
yet been reused at the point when the new object is created.
However, this is not as clear-cut for other methods of creating an object. For
instance, it is very clear that new-expressions obtain storage ([basic.life]/1)
and initialize the object ([expr.new]/21) only after the allocation function
returns. But at what point does it create the object? The standard appears to be
completely silent on this question. It would definitely be nice of the timing of
object creation could be clarified in general.
Thank you,
Matthew House
<std-discussion_at_[hidden]> wrote:
> Trying to make sense of the [basic.lifetime] section of the standard, but it
> appears to be contradictive in a few places.
>
> While I'm generally I'm aware that there is work still going on to further
> improve the standard, there is one particular contradiction that I haven't
> seen covered. http://eel.is/c++draft/basic#life-8 places restrictions in case
> the object lifetime ended but *before* the storage has been *released* or
> *re-used*. While mentioning *released* makes sense, *re-used*, in my opinion,
> brings contradiction since most often the new object would be created using
> placement new, and now the latest standard does clearly mention that storage
> is first released in placement new, and then the new object is created
> [http://eel.is/c++draft/basic#life-1.5]
A nitpick: storage is not *released* by a placement new-expression, it is only
*reused* when the new-expression obtains storage for the new object.
> Wouldn't the drop of *re-used* from http://eel.is/c++draft/basic#life-8 make
> it non-contradictive? My reasoning is that as *re-used* is defined at least
> for placement new (maybe memcpy as well?) the following code from the example
> in [basic.life]#8
>
> this->~C(); // lifetime of *this ends
> new (this) C(other); // new object of type C created. But why storage is not considered re-used given new rules in [basic.life]#1 around placement new
>
> would imply the following sequence of ordered events
> (1) end of a lifetime for the object which this used to point to ->
> (2) storage which this used to to point is *re-used* [basic.life]#1 ->
> (3) new object C is created at the storage which is used to point to
>
> Then if the above is correct, the condition is in [basic.life]#8 cannot apply
> to the corresponding example as (1) happens before (2) and (2) happens before
> (3). Now if (2) would have been dropped as the required condition then it
> would all make sense. However looks like from the comment in the example
> above, that the standard omits *re-use* in "new (this) C(other"
>
> Any thoughts on this? Or I've missed some important piece in my reasoning?
>
> Kind Regards,
> Mykola
Simply removing "reused" from [basic.life]/8 would be overly broad: if the
storage of a T object is reused for a U object, and then the storage of the U
object is reused for another T object, then I don't think it's intended for the
first T object to be transparently replaceable by the second. It would also have
to say that only the first object to reuse the storage is eligible for
transparently replacing the original object.
Otherwise, I agree that the wording is a bit weird. I think a lot of it comes
down to when exactly the new object is *created*, which is the precise operation
that [basic.life]/8 cares about. One possible interpretation can be derived from
the wording of [intro.object]/10, where implicitly creating objects "creates and
starts the lifetime" of those objects. If "starting the lifetime" of an object
includes obtaining its storage per [basic.life]/(1.1), then this implies that
creating an object occurs *before* obtaining its storage. Thus, under this
reading, there is no contradiction in [basic.life]/8, since the storage hasn't
yet been reused at the point when the new object is created.
However, this is not as clear-cut for other methods of creating an object. For
instance, it is very clear that new-expressions obtain storage ([basic.life]/1)
and initialize the object ([expr.new]/21) only after the allocation function
returns. But at what point does it create the object? The standard appears to be
completely silent on this question. It would definitely be nice of the timing of
object creation could be clarified in general.
Thank you,
Matthew House
Received on 2023-10-02 13:58:10