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

问题描述 投票:0回答:1

我遇到了一些代码类似于以下的函数,使用了一些模板化的类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()用于稍后被破坏的其他静态类的析构函数。 A的复杂结构显然可以延长A的寿命,超越任何其他物体的破坏,并且本身从未被破坏。我注意到https://en.cppreference.com/w/cpp/utility/program/exit

如果一个函数本地(块范围)静态对象被销毁,然后从另一个静态对象的析构函数调用该函数,并且控制流通过该对象的定义(或者如果间接使用,则通过指针或引用),行为未定义。

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

  • 根据C ++ 11标准(或更新版本),假设get_A是从具有静态生命周期的对象的析构函数调用的,那么单线程程序中的第一个版本在所有情况下都是合法的,或者它可能会强加未定义的行为?
  • 如何在不调用未定义的行为且不必更改其他类的get_A使用的情况下使此线程安全?我不希望为模板X实例化的每个可能的A都有初始化代码,因为A实例化了许多不同的类型。除非事实证明这是唯一的好解决方案。
c++ static thread-safety lifetime
1个回答
0
投票

我找到了解决方案:

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,函数当然可以像原始问题一样进行模板化,但我保持示例尽可能简单。

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

© www.soinside.com 2019 - 2024. All rights reserved.