The applicable rule for this is laid out in §3.8 [basic.life]/p1 and 4:
The lifetime of an object of type T
ends when:
- if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or
- the storage which the object occupies is reused or released.
4 A program may end the lifetime of any object by reusing the storage
which the object occupies or by explicitly calling the destructor for
an object of a class type with a non-trivial destructor. For an object
of a class type with a non-trivial destructor, the program is not
required to call the destructor explicitly before the storage which
the object occupies is reused or released; however, if there is no
explicit call to the destructor or if a delete-expression (5.3.5) is
not used to release the storage, the destructor shall not be
implicitly called and any program that depends on the side effects
produced by the destructor has undefined behavior.
So A *b = new (a) B;
reuses the storage of the A
object created in the previous statement, which is well-defined behavior provided that sizeof(A) >= sizeof(B)
*. That A
object's lifetime has ended by virtue of its storage being reused. A
's destructor is not called for that object, and if your program depends on the side effect produced by that destructor, it has undefined behavior.
The paragraph you cited, §3.8 [basic.life]/p7, governs when a pointer/reference to the original object can be reused. Since this code doesn't satisfy the criteria listed in that paragraph, you may only use a
only in the limited ways permitted by §3.8 [basic.life]/p5-6, or undefined behavior results (example and footnote omitted):
5 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 refers to the storage
location where the object will be or was located may be used but only
in limited ways. For an object under construction or destruction, see
12.7. Otherwise, such a pointer refers to allocated storage (3.7.4.2), and using the pointer as if the pointer were of type void*
, is
well-defined. Such a pointer may be dereferenced but the resulting
lvalue may only be used in limited ways, as described below. The
program has undefined behavior if:
- the object will be or was of a class type with a non-trivial destructor and the pointer is used as the operand of a
delete-expression,
- the pointer is used to access a non-static data member or call a non-static member function of the object, or
- the pointer is implicitly converted (4.10) to a pointer to a base class type, or
- the pointer is used as the operand of a
static_cast
(5.2.9) (except when the conversion is to void*
, or to void*
and
subsequently to char*
, or unsigned char*
), or
- the pointer is used as the operand of a
dynamic_cast
(5.2.7).
6 Similarly, 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 glvalue that refers to the
original object may be used but only in limited ways. For an object
under construction or destruction, see 12.7. Otherwise, such a glvalue
refers to allocated storage (3.7.4.2), and using the properties of the
glvalue that do not depend on its value is well-defined. The program
has undefined behavior if:
- an lvalue-to-rvalue conversion (4.1) is applied to such a glvalue,
- the glvalue is used to access a non-static data member or call a non-static member function of the object, or
- the glvalue is implicitly converted (4.10) to a reference to a base class type, or
- the glvalue is used as the operand of a
static_cast
(5.2.9) except when the conversion is ultimately to cv char&
or cv unsigned char&
, or
- the glvalue is used as the operand of a
dynamic_cast
(5.2.7) or as the operand of typeid
.
* To prevent UB from cases where sizeof(B) > sizeof(A)
, we can rewrite A *a = new A;
as char c[sizeof(A) + sizeof(B)]; A* a = new (c) A;
.