放置 "new" 可以用来改变 "const" 数据吗?
can placement "new" be used to alter "const" data?
[ 这是 . And Idiomatic Way to declare C++ Immutable Classes really gets at the issue, especially this 回答 "In a language designed around immutable data, it would know it can " 移动“你的数据,尽管它的(逻辑)不变性。”的后续行动。 ]
假设 struct
有 const
个成员
struct point2d { const int x; const int y; }; // can't change to remove "const"
持有指向 point2d
的指针的 class 可以指向具有不同值的新 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
会导致 (见评论)。
#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
?
您可以在对象的生命周期结束后重新使用存储。生命周期以析构函数调用结束。这在技术上没有任何问题。
在其生命周期结束后使用对象,正如我在
pt.~point2d();
new (&pt) point2d { pt.x + value, pt.y };
是未定义的行为。
如果您坚持将点 class 与 const
字段一起使用,您可以像这样解决这个问题:
void move_x( int const value )
{
auto const old_pt = pt;
pt.~point2d();
::new (&pt) point2d { old_pt.x + value, old_pt.y };
}
这可能感觉像是不必要的复杂化和可能的微观效率低下,但更确切地说,不必要的复杂化才是重点 class。
[ 这是
假设 struct
有 const
个成员
struct point2d { const int x; const int y; }; // can't change to remove "const"
持有指向 point2d
的指针的 class 可以指向具有不同值的新 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
会导致
#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
?
您可以在对象的生命周期结束后重新使用存储。生命周期以析构函数调用结束。这在技术上没有任何问题。
在其生命周期结束后使用对象,正如我在
pt.~point2d();
new (&pt) point2d { pt.x + value, pt.y };
是未定义的行为。
如果您坚持将点 class 与 const
字段一起使用,您可以像这样解决这个问题:
void move_x( int const value )
{
auto const old_pt = pt;
pt.~point2d();
::new (&pt) point2d { old_pt.x + value, old_pt.y };
}
这可能感觉像是不必要的复杂化和可能的微观效率低下,但更确切地说,不必要的复杂化才是重点 class。