The other answers are correct, but leave out one detail:
It is allowed if either the destructor or constructor are trivial. Other answers have clearly explained that if the destructor is trivial, the lifetime of the original object has not ended.
But if the constructor is trivial, then an object exists whenever a memory location of appropriate size and alignment is present. So even with a non-trivial destructor and trivial constructor, a brand-new object exists that you can call members on.
The verbiage that the other answers left out, that immediately precedes the end-of-lifetime rule they quoted, says
The lifetime of an object is a runtime property of the object. An object is said to have non-vacuous initialization if it is of a class or aggregate type and it or one of its members is initialized by a constructor other than a trivial default constructor. [ Note: initialization by a trivial copy/move constructor is non-vacuous initialization. — end note ] The lifetime of an object of type T
begins when:
- storage with the proper alignment and size for type
T
is obtained, and
- if the object has non-vacuous initialization, its initialization is complete.
An important note on usage of a new object trivially created in the storage of the old destroyed one: due to the trivial construction, no initialization has been performed on the data members and they now all have indeterminate value, so you must set their values (either through initialization, or invocation of an assignment operator that doesn't use the previous value) prior to reading any.
In the OP's situation, the original object still lives, so this caveat does not apply.