Consider the following 2 programs prog1 and prog2.Here if I try to change the value of the const
qualified variable i
1) Why are we allowed to change the value of a read-only
const
qualified variable in any circumstance? Doesn't it defeat the purpose of using aconst
qualifier?
Trying to change a const-qualified object through an assignment operator is a constraint violation:
6.5.16 under Constraints:
2 An assignment operator shall have a modifiable lvalue as its left operand.
Modifiable lvalues are defined in 6.3.2.1 (1):
A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const-qualified type.
As a constraint violation, it requires a diagnostic message from the compiler per 5.1.1.3 (1):
A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint, even if the behavior is also explicitly specified as undefined or implementation-defined. Diagnostic messages need not be produced in other circumstances.
But an implementation is not required to reject invalid programmes, so the diagnostic message could also be a warning instead of an error.
However, modifying an object that is declared const
through an lvalue that has not const-qualified type is not a constraint violation, although it invokes undefined behaviour, 6.7.3 (6):
If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.
Since it's not a constraint violation nor invalid syntax, it doesn't even require a diagnostic message to be emitted.
Shouldn't we get an error if we attempt to do so?
You must get a diagnostic message if you try to modify an object through an lvalue with const-qualified type.
Since that an egregious violation of the declared intentions, most compilers emit an error in these circumstances.
If you try to modify an object with const-qualified type through an lvalue with non-const-qualifed type as in
const int i=8;
int *ptr=&i;
*ptr=9;
the attempt to modify i
through the expression *ptr = 9
invokes undefined behaviour, but is not a constraint violation (or syntax error), hence does not require a diagnostic message (and none is given).
There is a diagnostic message emitted for the initialisation
int *ptr = &i;
because that is again a constraint violation, per 6.5.16.1 (1):
One of the following shall hold:
- the left operand has atomic, qualified, or unqualified arithmetic type, and the right has arithmetic type;
- the left operand has an atomic, qualified, or unqualified version of a structure or union type compatible with the type of the right;
- the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
- the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
- the left operand is an atomic, qualified, or unqualified pointer, and the right is a null pointer constant; or
- the left operand has type atomic, qualified, or unqualified _Bool, and the right is a pointer.
That diagnostic is however usually a warning and not an error because one may explicitly cast the const
away,
int *ptr = (int*)&i;
whereas one cannot cast away the const
from i
.
Modifying an object through a pointer to a non-const-qualified object type which has been obtained by casting away the const
from a pointer to a const-qualified object type is valid if the object pointed to is modifiable. Stupid example:
int i = 8;
const int *cptr = &i; // valid, no problem adding const
int *mptr = (int*)cptr;
*mptr = 9; // no problem, pointee is non-const
2) Even if due to some strange reason we are allowed to change values of constants, why the discrimination between changing the value of a read-only const qualified variable using a pointer (which is allowed,with a warning) and through using an assignment operation (which is simply not allowed and gives us an error)?
Directly assigning to an object with const-qualified type is not only a constraint violation, but also an obvious violation of the stated semantics. Declaring an object const
explicitly says "I don't want that object to be modified".
Modifying an object through a pointer to a non-const-qualified type is not a constraint violation, and only undefined behaviour if the pointee has const-qualified type. Converting a pointer to a const-qualified type to a pointer to the corresponding non-const-qualified type is allowed, and modifying the pointee through that pointer may be valid, therefore you get only a warning, and only if the conversion is not made explicit.
In the given short example, the compiler could detect that the pointee has const-qualified type and therefore the modification invokes undefined behaviour, but in general such would be difficult, and often impossible to detect. Therefore, the compiler doesn't even attempt to detect the simple cases, it's not worth the effort.
Why are we allowed to change the value of a read-only
const
qualified variable in any circumstance?
We aren't. I don't see why you assume that, nor which example shows that.
why the discrimination between changing the value of a read-only const qualified variable using a pointer (which is allowed,with a warning)
Again: it's not allowed, hence the warning. (Warnings are to be taken seriously - you don't seem to give any significance to them...) It's just that the compiler doesn't know that the pointer points to some const
-qualified object (because it's declared as non-const T *
).
As to why changing the variable works:
Explanation #1: it's undefined behavior (a constraint violation), so it can do anything.
Explanation #2: probably it's just stored on the stack like local automatic variables, and you can indeed change it.