C++ Logo

STD-DISCUSSION

Advanced search

Subject: Re: About the description of [basic.life]/6
From: Tadeus Prastowo (tadeus.prastowo_at_[hidden])
Date: 2020-04-14 12:27:53


On Tue, Apr 14, 2020 at 1:51 PM yo mizu via Std-Discussion
<std-discussion_at_[hidden]> wrote:
>
> I'm sorry to cut in on you, but I didn't understand why "pb" in the
> example meets the requirements of [basic.life]/6.
>
> On Fri, Apr 10, 2020 at 18:34 CDT Tadeus Prastowo via Std-Discussion
> <std-discussion_at_[hidden]> wrote:
> > This is not UB, including the example's *pb, because:
> > [basic.lval]/1.4 "An lvalue is a glvalue that is not an xvalue."
> > [basic.life]/6 "Indirection through such a pointer is permitted but
> > the resulting lvalue may only be used in limited ways, as described
> > below."
> > [basic.life]/7 "using the properties of the glvalue that do not depend
> > on its value is well-defined"
>
> The original example is as follows.
>
>
> #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

Quoting [basic.life]/6 with annotations:
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.

[The sentence above does not apply at this point in the example
because "the storage which the object occupied is reused".]

For an object under construction or destruction, see [class.cdtor].

[The sentence above does not apply at this point in the example
because no object is under construction/destruction.]

Otherwise, such a pointer refers to allocated storage
([basic.stc.dynamic.deallocation]), and using the pointer as if the
pointer were of type void*, is well-defined.

[The sentence above applies at this point in the example. Let's name
this sentence Sentence-A.]

Indirection through such a pointer is permitted but the resulting
lvalue may only be used in limited ways, as described below.

[The sentence above also applies at this point in the example. Let's
name this sentence Sentence-B.]

End quote.

> f(); // undefined behavior

UB because Sentence-A does not permit such a usage by [basic.life]/6.2

> ... = this; // OK, this points to valid memory

OK because Sentence-A permits such a usage by not listing the usage in
[basic.life]/6.1 or ... or [basic.life]/6.5.

Now, let's continue...

> }
>
> void g() {
> void* p = std::malloc(sizeof(D1) + sizeof(D2));
> B* pb = new (p) D1;
> pb->mutate();

... here.

> *pb; // OK: pb points to valid memory

OK because Sentence-B permits it by [basic.life]7.

> void* q = pb; // OK: pb points to valid memory
> pb->f(); // undefined behavior, lifetime of *pb has ended
> }
>
>
> Here is a simplified version.

I think the following version is harder, not simpler.

> #include <cstdlib>
>
> struct B {
> virtual void f();
> virtual ~B();
> };
>
> struct D1 : B { void f(); };
> struct D2 : B { void f(); };
>
> void g() {
> void* p = std::malloc(sizeof(D1) + sizeof(D2));
> B* pb = new (p) D1;
> pb->~B();

That I think is UB because the object is not yet constructed.

> // At this point, pb represents the address of the storage location
> where an object was located after the lifetime of the object has ended
> and before the storage which the object occupied is reused or
> released,
> // so pb meets the requirements of [basic.life]/6.
>
> new (pb) D2; // reuses storage
> // At this point, pb represents the address of the storage location
> where an object was located after the lifetime of the object has ended
> and after the storage which the object occupied is reused,
> // because a new object is created and reuses the storage,

The above just reuses the storage but does not construct an object.

> // so I think pb does not meet the requirements of [basic.life]/6.

After the explanation above, do you still think so?

> *pb;
> pb->f();
> }
>
>
> Thank you.

My pleasure.

-- 
Best regards,
Tadeus

STD-DISCUSSION list run by std-discussion-owner@lists.isocpp.org

Older Archives on Google Groups