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

2

6

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)
Turnspit answered 8/7, 2016 at 12:41 Comment(4)
This doesn't make much sense. If you want mutable points, why deliberately make them immutable?Ploughboy
That's what mutable means. The whole point of your point definition is that x and y never change once they've been initialized.Ploughboy
@Dan, what you are trying to do is undefined however you reach your result: either through memcpy or storing the object in a char[]. However you store or change those variables, as long as you refer to point::x or point::y, the compiler will assume they cannot be changed and optimize certain parts of your code. The same applies when you access to a point through a reference. However, as you show, a pointer will help.Dionysius
If you want something similar to C#'s mutable references to immutable objects, then a pointer is the way to go.Ploughboy
A
13

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.

Amylase answered 8/7, 2016 at 12:46 Comment(3)
No. Once something is const, by definition it cannot be changed. It is forever const.Amylase
@Dan Why do you insist on trying to mutate immutable data? If internal code needs to modify data that external code can't modify, what you need is encapsulation, not some language hacksDiapositive
Placement new will not work for the same reason. Nothing can be done to change the fundamental property that const data should be const, and the compiler is free to optimize away repeated references to the same const data. Whether the const-ness is subverted by memcpy(), or by placement new, makes no difference. Once a compiler sees const data, it is free to eliminate repeated references to it.Amylase
S
0

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?
Snowfall answered 8/7, 2016 at 12:53 Comment(2)
The question is whether you can safely modify the const members of a non const object. Your answer demonstrates that you cannot modify a const object.Dionysius
@FatihBAKIR Updated to discuss const members of non-const objects, but the idea is still largely the same. Using memcpy in a "defined" way such as this, you are not able to change const members. "Can" = probably, "should" = probably notSnowfall

© 2022 - 2024 — McMap. All rights reserved.