严格的别名规则和放置新

Strict Aliasing Rules and Placement New

IsoCpp.org 提供 FAQ 关于安置新:

他们提供的例子是:

#include <new>        // Must #include this to use "placement new"
#include "Fred.h"     // Declaration of class Fred
void someCode()
{
  char memory[sizeof(Fred)];     // Line #1
  void* place = memory;          // Line #2
  Fred* f = new(place) Fred();   // Line #3 (see "DANGER" below)
  // The pointers f and place will be equal
  // ...
}

由于 placememory 是不同的类型,但引用相同的内存位置,所以上面的代码不会违反 C++ 的严格别名规则吗?

(我知道 char 类型的指针可以别名任何其他类型,但这里我们似乎有一个 void* 别名 char*,这是我不允许的明白了吗?)

我怀疑大多数内存分配器也会以类似的方式违反严格的别名规则。使用 placement new 时遵守严格别名规则的正确方法是什么?

谢谢

What is the proper way to comply with the strict aliasing rule when using placement new?

正确的方法是使用std::aligned_storage。该代码示例不保证 Fred 的正确存储对齐,因此不应使用它。

正确的做法是:

#include <new>         // For placement new
#include <type_traits> // For std::aligned_storage

struct Fred {
  // ...
};

void someCode() {
  std::aligned_storage<sizeof(Fred), alignof(Fred)>::type memory;
  // Alternatively, you can remove the "alignof(Fred)" template parameter if you
  // are okay with the default alignment, but note that doing so may result in
  // greater alignment than necessary and end up wasting a few bytes.
  Fred* f = new(&memory) Fred();
}

Wouldn't the above code violate C++'s strict aliasing rule since place and memory are different types, yet reference the same memory location?

现在,关于您对原始代码中 fplacememory 之间的别名的担忧,请注意没有任何别名冲突。严格的别名规则意味着你不能“dereference a pointer that aliases an incompatible type”。由于您不能取消引用 void*(并且将指针 to/from 转换为 void* 是合法的),因此没有 place 导致严格的别名冲突的风险。

没有问题,因为代码没有引用 *place。仅仅让指针相等不会导致 UB - 它是通过它们间接访问的,这是被禁止的。

例如,以下是合法的:

 struct A {int x;} a;
 struct B {} *pb = reinterpret_cast<B*>(&a);
 A* pa = reinterpret_cast<A*>(pb);

请参阅 *pb,您违反了严格的别名规则。

当然,在您的特定示例中,您 不能 编写 *place,因为那样会产生 void 类型的左值,这是不允许的.

另请注意 Cornstalks 提出的观点:该示例确实需要使用 std::aligned_storage,因为无法保证 memoryFred 对象正确对齐。在实践中,这通常并不重要,因为您将使用 newmalloc 之类的东西为新的放置分配内存(return 适当对齐存储)。