Const as a supertype
May 9, 2017, 11:31 p.m.
My professor once told us that, following the common OOP polymorphism rules,
one can consider that the
volatile versions of a type (e.g. in C
or C++) to be the supertype of the non-qualified type.
It didn't make a lot of sense to me back then.
Usually when people explain super/subtyping, they think about substitutability.
If you can say with confidence that A is a B, then
A is a subtype of
As a common OOP example, a Student is a Person. You cannot say, always, that a
Person is a Student. This suggests a subtype relationship in one direction.
So putting my professor's words into that context, he is asserting that an int is always a const int, while a const int is not always an int.
This still made no sense - a red balloon is obviously a balloon, but a balloon is not always a red balloon. Intuitively, the qualifier should result in a subtype.
However, there is a more precise way (short of drawing arcane greek symbols) to
think about substitutability than the handwavy "is a" thing. If
A is a
B, then it should be the case that B can be "safely used"
wherever A is expected. Let's think about it in this context then.
If I have an
int, can I safely pretend that it's a
const int instead?
Actually, yes. That's why you can easily cast an
int to a
const int, but
not vice versa. You can do all the same read-only operations as before. The
read-only operations are what a
const int possesses; an
"inherited" those operations. Any write operations are only "defined" for the
int can do everything a
const int can, plus more.
What about volatile? That's a bit more subtle because the allowable operations
are seemingly the same, but it still makes sense if you consider what it means
int to safely pretend to be a
volatile int". When you add
volatile to a type, the compiler no longer saves the value into a register.
Each read or write will always go to memory (or more likely, the L[1-3]
caches). It makes sense then that always reading from memory is "safer" than
relying on the register value, which may be wrong if another thread or piece of
hardware changed the canonical value in the memory address. You can thus say an
int can safely pretend to be
volatile, but not the other way around.