Ben*_*Ben 13 c++ lifetime language-lawyer
这非常类似于这与Placement-new 和显式析构函数调用的正确用法,但范围更严格:
如果我有一个类型 ,S其std::is_nothrow_default_constructible_v<S>和std::is_nothrow_destructible_v<S>和not std::has_virtual_destructor_v<S>,not std::is_polymorphic_v<S>它是调用此函数的定义行为吗?:
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{};
}
Run Code Online (Sandbox Code Playgroud)
如果存在现有的指针或引用怎么办,如
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?
}
Run Code Online (Sandbox Code Playgroud)
我问这个的原因是std::once_flag没有重置机制。我知道为什么它通常不能,而且很容易误用,但是,在某些情况下我想进行线程不安全的重置,并且我认为这种重建模式会给我我想要的,只要这种重建是定义的行为。
https://godbolt.org/z/de5znWdYT
不幸的是,有时它是定义的行为,而其他时候您必须通过 运行指针std::launder。在许多情况下,编译器可能会假设对象没有更改,特别是const在 struct 中有引用或字段的情况下S。有关存储重用的cppreference 部分提供了更多信息。
S在您展示的特定代码中,如果是完整对象的基类,您将遇到问题,因为编译器可能会在某处缓存到错误的 vtable。
更详细地说,这是 C++17 和 C++20 之间发生的变化。在 C++17 及更早版本中,您需要调用std::launderwhen Scontains any const or reference fields。然而,尽管仍然存在其他限制,但C++20 中已经放宽了该特定要求。