严格的别名规则和放置新
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
// ...
}
由于 place
和 memory
是不同的类型,但引用相同的内存位置,所以上面的代码不会违反 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?
现在,关于您对原始代码中 f
、place
和 memory
之间的别名的担忧,请注意没有任何别名冲突。严格的别名规则意味着你不能“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
,因为无法保证 memory
与 Fred
对象正确对齐。在实践中,这通常并不重要,因为您将使用 new
或 malloc
之类的东西为新的放置分配内存(return 适当对齐存储)。
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
// ...
}
由于 place
和 memory
是不同的类型,但引用相同的内存位置,所以上面的代码不会违反 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?
现在,关于您对原始代码中 f
、place
和 memory
之间的别名的担忧,请注意没有任何别名冲突。严格的别名规则意味着你不能“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
,因为无法保证 memory
与 Fred
对象正确对齐。在实践中,这通常并不重要,因为您将使用 new
或 malloc
之类的东西为新的放置分配内存(return 适当对齐存储)。