(Note: "This question specifically relates to C++98" but the state of affairs is the same in all versions of the Standard, past and present (and future too I would bet), because it's essentially about const-correctness and preventing coders from opening a hole in the type system.)
As the Standard uses the general term "cv-qualifiers", I find it easier to understand when reasoning only with "const
" (no "volatile
") [but see further below for an example with volatile
]. The "C++ FAQ Lite" has a related entry : Why am I getting an error converting a Foo**
→ Foo const**
?
Essentially, allowing the conversion would let you silently modify a const T (through a pointer to non-const T):
int const theAnswer = 42;
int* p = 0; // int* p = &theAnswer; is not allowed of course...
int** pp = &p;
int const** ppc = pp; // <-- Error, but imagine this were allowed...
*ppc = &theAnswer; // &theAnswer is `int const*` and *ppc is `int const*` too,
// but it's also doing *pp = &theAnswer; i.e. p = &theAnswer;
*p = 999; // I.e. theAnswer = 999; => modifying a const int!
But by adding a "first-level" const
, the conversion int const* const* pcpc = pp;
is valid because the compiler will prevent you doing *pcpc = &theAnswer;
afterwards (because *pcpc
is const
).
Edit: As for volatile
, the problem is maybe less obvious than with const
, but allowing the conversion would let you silently incorrectly access (read or write) a volatile T as if it were not volatile (through a pointer to non-volatile T):
extern int volatile externTimer;
int* p = 0; // int* p = &externTimer; is not allowed...
int** pp = &p;
int volatile** ppv = pp; // <-- Error, but imagine this were allowed...
*ppv = &externTimer; // &externTimer is `int volatile*` and *ppv too,
// but it's also doing *pp = &externTimer; i.e. p = &externTimer;
int a1 = externTimer; // First read
int a2 = externTimer; // Second read, mandatory: the value may have changed externally
int b1 = *p; // First read
int b2 = *p; // Second read? may be optimized out! because *p is not volatile
But by adding a "first-level" const
, the conversion int volatile* const* pcpv = pp;
is valid because the compiler will prevent you doing *pcpv = &externTimer;
afterwards (because *pcpv
is const
).