问题
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