可以使用静态字符数组(线程安全)来延长对象的寿命吗?

Can a static array of chars be (thread-safe) used to prolong life of an object?

我遇到了一些具有类似于以下功能的代码,使用了一些模板 class A:

template<typename X>
A<X>* get_A() {
  static char storage[sizeof(A<X>)];
  static A<X>* ptr = nullptr;
  if(!ptr) { 
    new (reinterpret_cast<A<X>*>(storage)) A<X>();
    ptr = reinterpret_cast<A<X>*>(storage);
  }
  return ptr;
}

我需要使这个初始化线程安全,所以我将其更改为:

A<X>* get_A() {
  static A<X> a;
  return &a;
}

然而,这会导致段错误:get_A() 用于其他静态 classes 的析构函数中,这些静态 classes 稍后会被析构。 A 的复杂结构显然是为了延长 A 的生命周期而不会破坏任何其他对象,并且它本身永远不会被破坏。我注意到 https://en.cppreference.com/w/cpp/utility/program/exit

If a function-local (block-scope) static object was destroyed and then that function is called from the destructor of another static object and the control flow passes through the definition of that object (or if it is used indirectly, via pointer or reference), the behavior is undefined.

由于static storage和ptr不是'objects',我认为这条规则不会使第一个版本出现未定义行为,但它确实阻止了在函数内部构造一个static std::mutex。因此,我的问题是:

我找到了解决方法:

A* get_A()
{
  static typename std::aligned_storage<sizeof(A), alignof(A)>::type storage;
  static A* ptr = new (reinterpret_cast<A*>(&storage)) A();
  return ptr;
}

我已将问题中使用的 char 数组更改为 std::aligned_storage 以确保数组具有正确的对齐方式。在 C++17 中,它可能需要 std::launder,但我使用的是 C++11。键入 A 并且该函数当然可以像原始问题中那样进行模板化,但我使示例尽可能简单。

它仍然有点 hack,但据我所知这是线程安全的,它允许初始化静态对象,而不会破坏它并且不会泄漏内存(当然,只要 A没有自己的记忆)。