【发布时间】:2016-11-11 06:27:06
【问题描述】:
[这是can memcpy() be used to change “const” member data?的后续行动。而Idiomatic Way to declare C++ Immutable Classes 确实解决了这个问题,尤其是this 回答“在围绕不可变数据设计的语言中,它会知道它可以“移动”您的数据,尽管它的(逻辑)不变性。 ]
给定一个struct 和const 成员
struct point2d { const int x; const int y; }; // can't change to remove "const"
持有指向point2d 的指针的类可以指向具有不同值的新point2d 实例。
struct Bar
{
std::unique_ptr<point2d> pPt_{ new point2d{ 0, 0 } };
const point2d& pt() const {
return *pPt_;
}
void move_x(int value) {
pPt_.reset(new point2d{ pt().x + value, pt().y });
}
};
Bar 的客户见:
Bar bar; // (0, 0)
bar.move_x(3141); // (3141, 0)
point2d 和 Bar 都按预期工作;是的,point2d 是完全不可变的。
但是,我真的很想要Bar 的不同实现,它将point2d 实例存储为成员数据。有什么办法可以做到这一点?使用放置new 应该会导致undefined behavior(见评论)。
#include <new>
struct Baz
{
point2d pt{ 0, 0 };
void move_x(int value) {
// ** is this undefined behavior ? **
new (&pt) point2d { pt.x + value, pt.y };
}
};
不是否直接使用point2d 作为成员数据来解决(潜在?)未定义行为?
struct Blarf
{
unsigned char pt_[sizeof(point2d)];
const point2d& pt() const {
return *reinterpret_cast<const point2d*>(pt_);
}
Blarf() {
new (&pt_) point2d{ 0, 0 };
}
void move_x(int value) {
new (&pt_) point2d{ pt().x + value, pt().y };
}
};
哪个是正确的?只是Blarf?还是Baz 也可以?或者两者都不是,唯一的解决方案是Bar?
【问题讨论】:
-
当您链接到的“证明”与placement new 无关时,您为什么说placement new 结果在UB 中?我错过了什么?
-
哦,cmets,废话。不过,我不确定我是否同意 Sam。放置 new as-if 会破坏原件(如果它可以轻易破坏,则定义明确,例如
int)并用新的东西替换它。请注意,“对象”是内存中的一个区域。但它也有一个类型。两人在安置新人方面存在分歧。我认为关于 would 是否是 UB 的问题会很有趣。我会先问这个问题。 -
@Dan:我想不到,只要你解决了那个数组的潜在对齐问题,我想不出它有什么问题。
-
顺便说一句,这是一个非常愚蠢的
point2d类;我的哀悼 -
添加更多混乱 - 如果您显式调用对象的析构函数,然后使用放置 new 重新构建它,如:
pt.~point2d(); new (&pt) point2d{x, y};?看起来它实际上可能是一致的,因为原始对象的生命周期已明确结束,并在其位置重建了一个新对象...
标签: c++ constants immutability placement-new