在现有的 T 对象上放置新的 U 并操纵它是 UB?
在此链接中,存储重用部分显示以下示例。
void x()
{
long long n; // automatic, trivial
new (&n) double(3.14); // reuse with a different type okay
} // okay
和此处,其中一个答案包含此代码。
void f(float* buffer, std::size_t buffer_size_in_bytes)
{
double* d = new (buffer)double[buffer_size_in_bytes / sizeof(double)];
// we have started the lifetime of the doubles.
// "d" is a new pointer pointing to the first double object in the array.
// now you can use "d" as a double buffer for your calculations
// you are not allowed to access any object through the "buffer" pointer anymore since the // floats are "destroyed"
d[0] = 1.;
// do some work here on/with the doubles...
// ...
}
以上所有代码都试图在现有对象上执行放置新的另一种类型,第二个代码正在操作新对象。
但是,第一个链接说的是以下内容
如果在另一个对象占用的地址处创建一个新对象,则所有指针, 引用,原始对象的名称将自动引用新对象,并且, 一旦新对象的生命周期开始,就可以用来操作新对象,但前提是 原始对象可以透明地被新对象替换。
如果满足以下条件,则对象 x 可以透明地被对象 y 替换:
- y 的存储位置恰好覆盖 x 占用的存储位置
- y 与 x 具有相同类型(忽略顶级 cv 限定符)
强调一句,第二个例子是未定义行为,因为新对象类型与旧对象类型不同,d[0] = 1.;
操作新对象。
但是,没有人在评论中指出这个问题......所以我很困惑。
- 第一个示例定义的行为是因为它只是执行新的放置吗?
- 第二个例子是因为UB的原因而错误的吗?
我是不是误会了什么..?
In this link, Storage reuse section shows the following example.
void x()
{
long long n; // automatic, trivial
new (&n) double(3.14); // reuse with a different type okay
} // okay
and here, one of the answers contains this code.
void f(float* buffer, std::size_t buffer_size_in_bytes)
{
double* d = new (buffer)double[buffer_size_in_bytes / sizeof(double)];
// we have started the lifetime of the doubles.
// "d" is a new pointer pointing to the first double object in the array.
// now you can use "d" as a double buffer for your calculations
// you are not allowed to access any object through the "buffer" pointer anymore since the // floats are "destroyed"
d[0] = 1.;
// do some work here on/with the doubles...
// ...
}
All the above are trying to perform placement new another type on the existing object, and the second code is manipulating the new object.
But, the first link is saying the following
If a new object is created at the address that was occupied by another object, then all pointers,
references, and the name of the original object will automatically refer to the new object and,
once the lifetime of the new object begins, can be used to manipulate the new object, but only if
the original object is transparently replaceable by the new object.Object x is transparently replaceable by object y if:
- the storage for y exactly overlays the storage location which x occupied
- y is of the same type as x (ignoring the top-level cv-qualifiers)
According to the emphasized sentence, the second example is undefined behavior because the new object type is not the same as the old one, and d[0] = 1.;
manipulates the new object.
But, no one stated this problem in the comments.. so I'm confused.
- Is the first example defined behavior because it just performs placement new?
- Is the second example wrong because of UB?
Am I misunderstanding something..?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这两个例子都是合法的。
仅当您想要使用指向旧对象的指针来访问新对象而无需
std::launder
时,“透明可替换性”才有意义。但你没有这样做。您使用 Placement-new 返回的指针(而不是指向旧对象的指针)来操作新对象,它永远不需要
std::launder
。Both examples are legal.
"Transparent replaceability" only matters when you want to use a pointer to the old object to access the new object without
std::launder
.But you're not doing that. You're manipulating the new object using the pointer returned by placement-new (not a pointer to the old object), which never needs
std::launder
.