绕过c++严格的别名规则

Getting around c++ strict-aliasing rules

本文关键字:别名 规则 c++ 绕过      更新时间:2023-10-16

我想在一个容器中存储一定数量的项。我不想使用数组,因为我想避免不必要的对象构造函数调用。由于隐含的malloc调用,我不想使用std::vector(我想尽量减少对堆的访问,以实现最大的缓存一致性(。

所以我开始定义我自己的自定义容器,类似这样:

template<typename T, size_t capacity> class my_array {
  private:
   char buffer[sizeof(T)*capacity];
   ...
  public:
   T& operator[](size_t i) { return *(T*)&buffer[i*sizeof(A)]; }
   ...
};

但当我尝试实际使用这个容器时,我会收到编译器关于违反严格别名规则的警告。我读过关于严格混叠的文章,我理解为什么编译器优化会导致上面的代码中断。

我该如何解决这个问题?

奇怪的是,我的编译器对我的自定义object_pool类没有任何抱怨,除其他外,我将其用作关联STL数据结构的自定义分配器。该类看起来与上面的非常相似(使用char[]并执行类似的强制转换(。我搞不清两者之间的区别。

使用std::array

如果您不打算使用std:array,则

char buffer[sizeof(T)*capacity];

应该是

T buffer[capacity]

这就是为什么模板首先支持typename

如果您不想按照std::array的要求初始化对象,请使用std::aligned_storage。您可能会遇到对齐问题,因为您的结构可能被分配到一个奇怪的地址。

使用char数组别名将其用作存储应该是可以的,我不确定您的编译器在做什么。这真的是一个最小的测试用例吗?

我通过与我在原始帖子中提到的object_pool类进行精确比较,找到了一个神秘的解决方案。

我更换了

T& operator[](size_t i) { return *(T*)&buffer[i*sizeof(A)]; }

带有

T* operator[](size_t i) {
  if (...) {
    return (T*)&buffer[i*sizeof(A)];
  }
  return NULL;
}

其中,...是我知道的任何谓词,它将始终求值为true,但编译器不够聪明,无法实现它,它将总是求值为true

我很好奇是否有人能在这里准确地解释编译器的想法。我使用的是gcc 4.4.3。