memcpy() 可以用来更改"const"成员数据吗?

can memcpy() be used to change "const" member data?

本文关键字:const 成员 数据 memcpy      更新时间:2023-10-16

对于具有const成员的struct

struct point { const int x; const int y; };

用作成员数据

struct Foo
{
    point pt{ 0, 0 };
    void move_x(int value);
};

如何编写Foo::move_x()来更新Foo::pt? 可以使用memcpy()吗?

#include <memory.h>
void Foo::move_x(int value)
{
    const point pt_{ pt.x + value, pt.y };
    (void) memcpy(&pt, &pt_, sizeof(pt_)); // pt = pt_;
}

这可以使用指针安全地完成

#include <memory>
struct Bar
{
    std::unique_ptr<point> pPt_{ new point{ 0, 0 } };
    const point& pt() const {
        return *pPt_;
    }
    void move_x(int value) {
        pPt_.reset(new point{ pt().x + value, pt().y });
    }
};

但是point总是存储在堆上而不是存储在Bar中。

请注意,客户端根本不关心point

   Foo foo;
   foo.move_x(314); // (314, 0)
   Bar bar;
   bar.move_x(3141); // (3141, 0)

这显然是未定义的行为。现在,"可以"做到这一点吗?当然,它可以,但只是在编译器不会抱怨的意义上。但是C++的一个方面只是因为编译器不抱怨,并不意味着生成的代码可以正常工作。

有人编写一些读取pt、调用move_x()然后再次读取pt的代码只是时间问题。

现代的优化编译器会正确地假设,因为代码正在读取const实例,所以它们的值不能改变,然后继续优化从pt的第二次读取,使用从 CPU 寄存器中第一次读取pt缓存的数据。

然后,倒霉的程序员将花一周的时间试图弄清楚为什么代码显然没有做程序所说的应该做的事情。

尝试将memcpyconst成员一起使用时,您将收到编译器错误,如下所示:

#include <cstdlib>
#include <cstdio>
#include <cstring>
struct foo
{
    public:
        foo(int init_x);
        const int x;
        int bar();
};
foo::foo(int init_x): x(init_x) {}
int foo::bar()
{
    int *y = (int *) malloc(sizeof(int));
    *y = 6;
    memcpy(&x, y, sizeof(int));
    return x;
}
int main(int argc, char *argv[])
{
    foo f{5};
    printf("%dn", f.bar());
}

以上结果是

error: invalid conversion from ‘const void*’ to ‘void*’ [-fpermissive]
    memcpy(&x, y, sizeof(int));

虽然我在这个例子中使用了const int,但如果你改用const int指针成员,你会发现相同的结果,即

const int *x;

但是,如果删除const描述符并使用:

int x;

(或int *x;,就此而言)错误不再发生,并且程序打印6 ,正如人们所期望的那样。

所以这就引出了一个问题,如果你知道某件事将被声明为const

  • 如果你有能力改变声明,你不能自己删除const吗?
  • 如果你不能改变声明,你有充分的理由想要打破const做出的"承诺"吗?