https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86908

--- Comment #7 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Richard Biener from comment #6)
> (In reply to Jonathan Wakely from comment #1)
> > No, your program has undefined behaviour. To make it valid you either need
> > to use std::launder, or use the pointer that is returned by the placement
> > new.
> > 
> > The compiler is allowed to assume that the dynamic type of &strategy does
> > not change (that's why you need to use std::launder).
> 
> You need to use std::launder at every use of the object, right?

Or just launder the pointer once and then use that:

  auto laundered = std::launder(&strategy);
  laundered->doIt();  // 

> I wonder
> why static_cast<BaseStrategy*>() isn't a good enough "hint" here that
> the type of strategy changed (from QOI perspective, std::launder is new).

Casting from type X* to Y* where X and Y happen to be the same type can happen
in generic code that was already written years ago, and doesn't play these sort
of dirty tricks. It would be a shame to prevent useful optimisations because of
a no-op cast.

We don't need to make it easier to play nasty games with the type system. There
are ways to do this sanely, without undefined behaviour, even without
std::launder (just using the pointer returned by the placement new-expression
should be good enough, instead of accessing through &strategy every time).

> I guess a better testcase would be to declare strategy as AStrategy
> and placement-new BaseStrategy over it or does the standard guaranteee
> that AStrategy fits in the storage of BaseStrategy?

For these types, with the Itanium ABI, it fits, and you could add a
static_assert to verify it.

Either way you need to placement new the original type back again, otherwise
the wrong destructor gets called on scope exit, which adds more undefined
behaviour.

Reply via email to