放置"new"是否可以用于更改"const"数据?

can placement "new" be used to alter "const" data?

本文关键字:const 数据 new 是否 放置 用于      更新时间:2023-10-16

[ 这是 memcpy() 可以用来更改 "const" 成员数据的后续吗? 声明不可变类C++习惯性方法确实解决了这个问题,尤其是这个答案"在围绕不可变数据设计的语言中,它会知道它可以"移动"你的数据,尽管它(逻辑)不可变。


const成员一起struct

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)

point2dBar都完全按照预期工作;是的,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 };

是未定义的行为。

如果您坚持使用带有const字段的点类,则可以像这样解决方法:

void move_x( int const value )
{
     auto const old_pt = pt;
     pt.~point2d();
     ::new (&pt) point2d { old_pt.x + value, old_pt.y };
}

这可能感觉像是不必要的复杂性和可能的微效率低下,但相反,不必要的复杂性是点类。