Home > Software design >  Is it defined behavior to explicitly call a destructor and then use placement new to reconstruct it?
Is it defined behavior to explicitly call a destructor and then use placement new to reconstruct it?

Time:07-09

This is very similar to Correct usage of placement-new and explicit destructor call but tighter in scope:

If I have a type, S, for which std::is_nothrow_default_constructible_v<S> and std::is_nothrow_destructible_v<S> and not std::has_virtual_destructor_v<S> and not std::is_polymorphic_v<S>, is it defined behavior to call this function?:

template <typename T>
void reconstruct(T& x) noexcept {
    // C  20: requires instead of static_assert:
    static_assert(std::is_nothrow_default_constructible_v<T>);
    static_assert(std::is_nothrow_destructible_v<T>);
    static_assert(!std::has_virtual_destructor_v<T>);
    static_assert(!std::is_polymorphic_v<T>);
    x.~T();
    ::new (&x) T{};
}

What if there are existing pointers or references to, as in

int main() {
    S s;
    s.x = 42;
    const S& sref = s;
    reconstruct(s);
    return sref.x; // Is this UB because the original S sref references no longer exists?
}

My reason to ask this is that std::once_flag has no reset mechanism. I know why it generally can't and it would be easy to misuse, however, there are cases where I'd like to do a thread-unsafe reset, and I think this reconstruction pattern would give me what I want provided this reconstruction is defined behavior. https://godbolt.org/z/de5znWdYT

CodePudding user response:

Unfortunately, sometimes it is defined behavior, and other times you have to run the pointer through std::launder. There are a number of cases where the compiler may assume that the object hasn't changed, particularly if there are references or const fields in struct S. More information is available in the cppreference section on storage reuse.

In the particular code that you show, you will run into problems if S is a base class of the complete object, because the compiler could, for example, be caching to wrong vtable somewhere.

  • Related