3

Given the following code:

class TestA
{
    private:
        char Temp;

    public:
        char *Ptr;

        TestA(){Ptr = NULL; Temp = 'A'; Ptr = &Temp;}
        void Function(){Ptr = &Temp; Temp = 'B';}

        void operator=(const TestA &ItemCopy)
        {
            //ItemCopy.Temp = 'N'; //Not permitted
            printf("%c!\n",ItemCopy.Temp);
            Ptr = ItemCopy.Ptr; //This is okay
            *Ptr = 'M'; //This is okay, but it re-assigns ItemCopy.Temp. What?
            printf("%c!\n",ItemCopy.Temp);
        }
};

int main()
{
    TestA Temp1,Temp2;

    Temp1.Function();
    Temp2 = Temp1;
}

Produces the following:

B
M

Even though ItemCopy is const. Why am I permitted to indirectly modify it or even take a non-const copy of the pointer?

SE Does Not Like Dissent
  • 1,767
  • 3
  • 16
  • 36
  • You are not, I think this is undefined behavior. – Karel Petranek Oct 15 '11 at 13:04
  • 1
    some rules in C++ are more like recommendations, you can circumvent if you want - for good or worse. – AndersK Oct 15 '11 at 13:07
  • 2
    @dark_charlie: Why do you think it is _undefined behavior_. (I don't think it is but you may have spotted something that I haven't.) – CB Bailey Oct 15 '11 at 13:08
  • 2
    @Anders K.: not quite. They're more like road signs telling you where the road ends. It's not *illegal* to drive off the road, it just means that you're no longer operating within the constraints and guarantees offered by the road (such as "you won't suddenly drive off a cliff", or "there won't be any sudden trees in front of you". Break the rules of C++, and you've gone off the road. It's not "illegal", your program just isn't C++ any more, and you can't assume that *any* part of your program is going to still behave like C++. – jalf Oct 15 '11 at 13:14
  • jalf: that's a great metaphor, really. Your comment should be wiki-ized or otherwise passed down to generations to come :) – rubenvb Oct 15 '11 at 13:24

5 Answers5

9

Because ItemCopy is const, ItemCopy.Ptr has and effective type of char * const. The pointer is const but the item pointed to can be modified. This means that the assignment:

*ItemCopy.Ptr = 'M';

is meaningful and allowed (the underlying object is not itself const), it is also legal to copy the pointer and assign through it as you have done. A direct assignment ItemCopy.Temp = 'M' would not be legal but that doesn't meant that you can't modify the variable ItemCopy.Temp if there is another non-const access path as you have.

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • Is there any way to make it so during assignment, it's const char * const? – SE Does Not Like Dissent Oct 15 '11 at 13:13
  • @SSight3: You can make the member variable `const char*` but I thought that your example was an artificial demonstration. Do you want to achieve something specific? I don't see a good reason to have a member variable that points at another member variable in the same class instance and it's almost certainly a bad design to make it public. – CB Bailey Oct 15 '11 at 13:17
  • This is artifical. I am testing out assumptions before I build a proper class, and just want to make sure I know as many options as possible. – SE Does Not Like Dissent Oct 15 '11 at 13:19
2

Pointer aliasing. By assigning ItemCopy::Ptr to this->Ptr you alias the pointer and through it assign to the other value. Also remember the rule of 3 when writing things like this.

Community
  • 1
  • 1
pmr
  • 58,701
  • 10
  • 113
  • 156
2

Ptr points to ItemCopy.Ptr, which in turn points to Temp. So when you dereference it, you'll be writing to Temp.

Tony The Lion
  • 61,704
  • 67
  • 242
  • 415
1

The semantics of:

    const TestA &ItemCopy

can only guarantee the pointer member ItemCopy.Temp itself cannot be directly modified, what the pointer points to is not guaranteed to be const.

0

It's because const rules are applied at compile time, but this circumvention of it is a result of runtime state. Making a non-const copy of a member of a const referenced object does not modify the object and thus does not violate the const reference. This is because as far as the compiler knows, all you have done is made a copy of a value, the address of a piece of mememory. It does not predict that you might dereference it here or elsewhere - it does not apply constness to memory locations, but to identifiers, and you have not violated that yet.

At runtime, you assign the non-const ptr an address that also happens to be referenced by a const identifier, but there is no compile time connection between the two, because the constness only applies to one of the ways to access it, and even then, only in the scope of this one function. It can't treat your non-const pointer as const in some cases and non-const in others based on runtime state, that would violate the semantics of C++ in other ways.

kylben
  • 349
  • 1
  • 6