Why is it allowed in C++ to modify a constant object's pointer member variable's memory from outside?
Asked Answered
P

4

7

I've been trying to understand when I write a function in C++ with a constant argument and a pointer variable inside of that object than the const flag is not protecting the underlying memory against modifications. For example it's perfectly legal to do the following in the operator=() function of the class called X:

class X
{
public:
    X& operator=(const X& other)
    {
        this->data = other.data; //(*)
        return *this;
    }

private:
    int* data;
};

(*): This is the same as the following:

int* some_pointer;
int* const other_pointer = some_pointer;
int* class_pointer = other_pointer;

But not the same as:

const int* other_pointer; 
int* class_pointer = other_pointer;

Which would generate the following error:

error: invalid conversion from 'const int*' to 'int*' [-fpermissive]
 int* class_pointer = other_pointer;
                      ^

I understand why other.x is being casted to a int* const but I don't see why it isn't being casted to a const* int at the same time (which is a const int* const). When I write a function with a const argument my logic suggests anything inside that argument should inherit the constness because that should be the purpose of const, to protect the underlying data against modification.

When a pointer member is being accessed from outside of the const version of a class I think it should be a reasonable expectation that the object's const keyword should protect anything (even the memory) that gets out of the class from modification. The argument against this is that the outside memory does not belong to the object so it shouldn't be it's responsiblity to protect it either. My perspective on it is that in this case (out of any other cases when it's being accessed somewhere else with any kind of access rights) we are taking something out of a const object. In other words it's giving visibility to something outside of itself. So what's the reason behind not making the visibility const? That wouldn't change the accessibility rights of the memory in any other location of the code!

Picador answered 5/8, 2015 at 12:56 Comment(10)
I am not really sure what you're asking here. Care to clarify?Dissemblance
I'm asking why other.data (in the function X::operator=()) is not being casted to const int* const instead of just int *const and what was the design decision behind this in C++.Picador
Because that wouldn't make any sense.Dissemblance
@juanchopanza, "wouldn't make any sense"? That's exactly what const does in the D programming language.Musclebound
@AaronMcDaid That doesn't automatically make it make sense in C++. Raw pointers have know nothing about ownership. Ownership is something a class designer builds on top of it.Dissemblance
C++17 has a proposal for const propagating pointers. en.cppreference.com/w/cpp/experimental/propagate_constDeering
@juanchopanza, I wasn't implying any advocacy for the design in D. Just an observation that D did it. However, the OP's original question still remains unanswered: why did C++ take a different approach from that taken by D?Musclebound
@AaronMcDaid Because an object does not always own objects pointed by their members. By modifying the object pointed by the pointer member of the const object, you might not be modifying the object at all. It is the designer's responsibility to ensure const correctness is preservedDeering
@KABoissonneault, you're just restating how C++ works. The OP wants to know why it was designed like this.Musclebound
@AaronMcDaid As I said, it was (probably) designed like this to allow more flexibility to class designers, because "an object does not always own objects pointed by their members". I'm not a fan of repeating myself when there's a misunderstanding, but I don't understand where you misunderstand my post. I'm definitely not doing what you're saying.Deering
M
5

"When I write a function with a const argument my logic suggests anything inside that argument should inherit the constness because that should be the purpose of const, to protect the underlying data against modification."

You are quite right about this, however the pointer stored inside the X-object points outside the object. The outside isn't affected by X's constness, just the data stored inside X.

Matthiew answered 5/8, 2015 at 13:34 Comment(4)
I totally agree but it should be const X's obligation to provide a constant visibility to that "outside" memory if C++ would really treat X and everything in X as something which is unmodifiable. That wouldn't affect the outside memory at all!Picador
I guess it comes down to whether the "outside" data is owned by the current class. If yes, then it is not really outside data at all. Maybe the OP's original question is "why aren't member pointers assumed to be pointers to owned data, and therefore const-propogating?"Musclebound
... (continued) In C++, we already have the mutable keyword to allow use to mutate members within const methods - the intention is that these changes won't change the 'logical' const-ness of the methods. So perhaps pointers should have been const-propogating by default, but with mutable to change this where necessary (including non-owned). (Of course, we're about 30 years too late to do any of this!)Musclebound
@AaronMcDaid That's a good point. But a lot of things in C++ were designed in ways that we now know are backwards, where the easy way to do things is the wrong to do things. We know that now because we have much more experience with the language. But fortunately, there are multiple other modern languages out thereDeering
A
2

Why do you think that, because the pointer is constant, that which is pointed to should be constant? It does not necessarily follow.

Sometimes you need a pointer that must always point to a particular place, but through which you can modify the indicated place. There's nothing about class X which suggests that you shouldn't be able to change the memory data points to.

More importantly, you're thinking wrongly about the const keyword. Given

X& operator=(const X& other)

all you are doing is telling the compiler that you do not intend to change other inside operator=() and asking the compiler to prevent you from doing so in case you forget. Nothing more, nothing less. It says nothing at all about the const-ness of other outside of operator=() nor of the const-ness of anything any pointer inside other points to.

Aphotic answered 5/8, 2015 at 15:12 Comment(0)
G
0
int* const other_pointer
declare other_pointer as const pointer to int

as opposed to:

const int* other_pointer
declare other_pointer as pointer to const int

courtesy of http://cdecl.org/

Note the difference in the placement of const.

Gman answered 5/8, 2015 at 13:1 Comment(1)
OP seems aware of this.Tehuantepec
V
0

You're not changing the value of other.data, so there's no const violation. You could certainly modify the object that other.data points to, but that's beyond the compiler's responsibility to enforce const-correctness.

Vermicide answered 5/8, 2015 at 14:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.