使用aligned_storage时如何避免严格的别名错误

mar*_*ack 5 c++ strict-aliasing c++11

我正在使用std::aligned_storage变体模板作为后备存储.问题是,一旦我-O2在gcc上启用,我就会开始收到"取消引用类型 - 惩罚指针会打破严格别名"的警告.

真实模板要复杂得多(在运行时检查类型),但生成警告的最小示例是:

struct foo
{
  std::aligned_storage<1024> data;

  // ... set() uses placement new, stores type information etc ...

  template <class T>
  T& get()
  {
    return reinterpret_cast<T&>(data); // warning: breaks strict aliasing rules
  }
};
Run Code Online (Sandbox Code Playgroud)

我很确定boost::variant与此基本上是一样的,但我似乎无法找到他们如何避免这个问题.

我的问题是:

  • 如果aligned_storage以这种方式使用违反严格别名,我应该如何使用它?
  • 实际上是否存在严格别名问题,get()因为函数中没有其他基于指针的操作?
    • 如果get()被内联呢?
    • 怎么样get() = 4; get() = 3.2?由于intfloat不同的类型,该序列可以重新排序吗?

ric*_*ici 5

std::aligned_storage是的一部分<type_traits>; 与该头文件的大多数其他居民一样,它只是某些typedef的持有者,并不打算用作数据类型.它的工作是采用尺寸和对齐方式,并使您成为具有这些特征的POD类型.

你不能std::aligned_storage<Len, Align>直接使用.您必须使用std::aligned_storage<Len, Align>::type转换后的类型,它是"POD类型,适合用作任何大小最多Len且对齐为除数的对象的未初始化存储Align." (Align默认为大于或等于的最大有用对齐Len.)

正如C++标准所指出的那样,返回的类型通常std::aligned_storageunsigned char带有对齐说明符的数组(具有指定大小).这避免了"无严格别名"规则,因为字符类型可能是任何其他类型的别名.

所以你可以这样做:

template<typename T>
using raw_memory = typename std::aligned_storage<sizeof(T),
                                                 std::alignment_of<T>::value>::type;

template<typename T>
void* allocate() { return static_cast<void*>(new raw_memory<T>); }

template<typename T, typename ...Arg>
T* maker(Arg&&...arg) {
   return new(allocate<T>()) T(std::forward<Arg>(arg)...);
}
Run Code Online (Sandbox Code Playgroud)

  • 这种情况是*其他类型*别名字符类型.严格别名规则不是对称的,它禁止*其他类型*别名字符类型,但它允许字符类型别名*其他类型*(这里没有发生).但是,[此答案](http://stackoverflow.com/questions/13466556/aligned-storage-and-strict-aliasing)认为`aligned_storage`不会因其他原因违反严格别名(尽管我不太关注它的论点). (5认同)