C++ Logo

std-discussion

Advanced search

Re: About the description of [basic.life]/6

From: merukun1125_at <merukun1125_at_[hidden]>
Date: Sat, 11 Apr 2020 01:29:41 +0900 (JST)
On Fri, Apr 10, 2020 at 10:28 PM Tadeus Prastowo via Std-Discussion <
std-discussion_at_[hidden]> wrote:
>>
>> Inspired by: https://teratail.com/questions/250362 and
>> https://stackoverflow.com/questions/56977042/reusing-objects-space-by-another-object.
>>
>> At the beginning of [basic.life]/6
>> <https://timsong-cpp.github.io/cppwp/n4659/basic.life#6>, the following was written:
>>
>> Before the lifetime of an object has started but after the storage which
>> the object will occupy has been allocated or, after the lifetime of
>> an object has ended and before the storage which the object occupied is reused or
>> released, any pointer that represents the address of the storage location where the
>> object will be or was located may be used but only in limited ways.
>>
>> However, the example in [basic.life]/6 also describes a pointer to the
>> storage which the new object was created.

> Could you copy and paste the example, please?

Thank you for your reply. Here is an example.


[ Example:
#include <cstdlib>struct B { virtual void f(); void mutate(); virtual ~B();};struct D1 : B { void f(); };struct D2 : B { void f(); };void B::mutate() { new (this) D2; // reuses storage — ends the lifetime of *this f(); // undefined behavior ... = this; // OK, this points to valid memory}void g() { void* p = std::malloc(sizeof(D1) + sizeof(D2)); B* pb = new (p) D1; pb->mutate(); *pb; // OK: pb points to valid memory void* q = pb; // OK: pb points to valid memory pb->f(); // undefined behavior, lifetime of *pb has ended} — end example ]

>> Does the description of [basic.life]/6, especially the description after the
>> "otherwise" clause, also say a pointer to the storage which another new object was created?
>>
>> Furthermore, does (a) to (f) in the following code cause undefined behavior?
>>
>> #include <cstdint>
>> #include <new>
>>
>> int main()
>> {
>> std::uint32_t* p32 = new std::uint32_t[4]{};

> At this point, the four objects of type std::uint32_t have already
> been alive due to the use of `{}'.

>> std::uint16_t* p16 = new (p32) std::uint16_t[4]{};

> At this point, the four objects of type std::uint32_t have ended their
> lives due to storage reuse.
> And, the four objects of type std::uint16_t have already been alive
> due to the use of `{}'.

>> *p32; // (a) (indirect references of type void* are not allowed)

> UB because the four objects of type std::uint32_t have ended their
> lives due to storage reuse.

In the [basic.life]/6 example, I do not know why `*pb` is not UB.

>> std::uint32_t lval = *p32 + 100; // (b)

> UB due to `*p32' for the reason given above.

>> std::uint32_t* p32_2 = p32 + 2; // (c)

> This is okay because p32 is a valid pointer.

>> *p32_2 = 0; // (d)

> UB for the same reason `*p32' is UB.

>> p32 = new (p32) std::uint32_t[4]; // (e)

> At this point, the four objects of type std::uint16_t have ended their
> lives due to storage reuse.
> The four objects of type std::uint32_t, however, have not been
> constructed due to missing `{}'.

>> delete [] p32; // (f)

> This deallocates the storage. Not UB because std::uint32_t has no
> non-trivial destructor.

>> }
>>
>> Thank you.

--
Best regards,
merukun1125

Received on 2020-04-10 11:32:37