but from XXX to const XXX should be OK
No, this isn't okay in this specific instance. Consider:
int const x = 10; // implementation stores to read-only memory
// implementation crashes on writes to read-only memory
void foo(int const **ptr) {
*ptr = &x;
}
int main() {
int *p;
foo(&p);
*p = 12; // crash
}
If this were legal it would assign a 'pointer to const' value to a 'pointer to non-const' object, and therefore enable dangerous writing to constant objects.
For a conversion to const to be okay const must be added at every level in the type above where the lowest const is added (except the very top).
For example it's not okay to convert int ******
to int ***const***
, but it is okay to convert it to int ***const*const*const*
. This also applies to volatile
: you can convert int ******
to int ***volatile*const*const*
but not int ***volatile***
This rule in the type system protects us from mistakenly treating const objects as non-const, or volatile objects as non-volatile, and if we really want to make this mistake then we have to use const_cast
.
foo(const_cast<int const **>(&p));
*p = 12; // crash
With const cast the program is well formed and the compiler happily produces an executable that exhibits undefined behavior. (live example)
Fixing foo()
to allow it to take a pointer to non-const:
void foo(int const * const *ptr) {
*ptr = &x; // error, can't modify *ptr
}
foo(&p); // conversion works fine
prevents foo()
from writing a 'pointer to const' value into a 'pointer to non-const' object. (live example)
You probably got the idea that XXX to const XXX is okay because the most common case of this, i.e. with single level pointers: int *
-> int const *
, is okay and also obeys the above conversion rule. const
at the very top level doesn't matter because changes to the parameter itself won't escape the function.