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
has_virtual_destructor_v<S> == false
as well. – Anabelles
is a instance of a subclass ofS
: There need not be a virtual destructor forS
which could result in the object being improperly destroyed and "partially resurrected" as a different object type. Any access to functionality of the subtype inmain
would necessarily be UB. – HellS
containsconst
data (or a reference)? – Niloticreturn sref.x
would be UB if S were a trivial-layout struct with no constructor or member initializer -- it would be UB because it's uninitialized. However, I don't think you can callreconstruct
this way, passing aconst S &
to a non-const reference parameter. – Anabellestd::launder
and the world of placement-new
ing things in raw memory. – Niloticreconstruct
a template and everything compiled, so you should edit your question to correspond to that. I can't; the queue is full. – AnabelleS
has any const or reference members then you can't use this technique. – Auntiestd::is_polymorphic
, sincereconstruct
might not do the right thing for polymorphic types. – Sophiststd::unordered_map
relied on that restriction viastd::pair<const key_type, value_type>
to guarantee that keys didn't change hash values while inside the container (which would result in never being able to find them). Third-party hashtables, caching layers, etc will likewise have the rug yanked out from under them. The incompatibility is not a change in the behavior of valid say C++14 code, it's inability to compose code written for C++14 with new C++20 code. – Anisotropic&*umap.find(k)
? I understand why the key part of thestd::par
has to beconst
but how does the rule-change affect it? What was valid in C++14 but breaks in 20? – Niloticstd::pair<const key_type, value_type>
and the code doing the reconstruct. I'll repeat the takeaway: The incompatibility is not a change in the behavior of valid say C++14 code, it is inability to safely compose code valid when written for C++14 with new valid C++20 code. – Anisotropic