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

核能气质少年 提交于 2020-02-02 03:30:07

问题


For a struct with const members

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

that is used as member data

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

How can Foo::move_x() be written to update Foo::pt? Is it OK to use 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_;
}

This can be safely done using a pointer

#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 });
    }
};

but the point is then always stored on the heap rather than in Bar.

Note that clients don't really care about point at all:

   Foo foo;
   foo.move_x(314); // (314, 0)

   Bar bar;
   bar.move_x(3141); // (3141, 0)

回答1:


This is clearly undefined behavior. Now, "can" this be done? Sure, it can, but only in the sense that the compiler will not complain. But one aspect of C++ is just because the compiler doesn't complain it doesn't mean that the resulting code will work right.

It's only a matter of time before someone writes some code that reads pt, calls move_x(), and then reads pt again.

A modern, optimizing compiler will rightfully assume that because the code is reading const instances, their values cannot change, and then proceed and optimize away the second read from pt, using the data cached from the first read of pt, in the CPU registers.

And then, the hapless programmer will spend a week trying to figure out why the code is clearly not doing what the program says it should be doing.




回答2:


You will get a compiler error trying to use memcpy with a const member as follows:

#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("%d\n", f.bar());
}

The above results in

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

While I used const int in this example, you will find the same result if you instead use a const int pointer member, i.e.

const int *x;

But, if you remove the const descriptor and use:

int x;

(or int *x;, for that matter) the error no longer happens, and the program prints 6, as one might expect.

So this begs the question, if you know something is going to be declared as const:

  • If you have the ability to change the declaration, couldn't you remove const yourself?
  • If you can't change the declaration, is there a good reason you're looking to break the "promise" that const makes?


来源:https://stackoverflow.com/questions/38266973/can-memcpy-be-used-to-change-const-member-data

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!