Thursday, June 23, 2011

Short little post about C++ const-correctness

Ok, so I don't really like the concept of "const-correctness" in c++, but I never really had a solid reason to dislike it besides laziness. Hence I never really bother to mark class functions as const (except in basic numerical types like Vec2D) unless the compiler whines about it.

Well, until yesterday that is. I was writing a rational number class for the hell of it (mostly as a little exercise for myself). Internally, it's represented as a numerator and denominator value. Now here's the thing: Rational(10, 4) and Rational(5, 2) are effectively identical and behave exactly the same way as far as any user could tell, even if they're represented different internally. A function, Rational::reduce(), changes the internal representation of the number without actually changing the value of the number. For all intents and purposes, this function is constant.

A few operators require reducing the rationals first, notably operator== (which most would agree should be a constant function).

Yet, reduce can't be labeled const because it changes numerator and denominator, therefore operator== either has to work with temporaries or also can't be labeled const.

Solutions:
- Marking numerator and denominator as mutable, but that's just stupid and unsafe since at this point you basically completely throw the notion of const correctness out the window and you may as well just not mark anything as const [which ends up happening in most of my personal projects].

- const_cast<Rational*>(this)->numerator = blahblahblah
[this is quite ugly, though it does work]

-
void reduce(){ //reducenumbercodehere }
void reduce() const {const_cast<Rational*>(this)->reduce();}
[this is also ugly and boilerplate-y]

//////////////////////
What c++ SHOULD have is a way to mark a function as "mutable const". We can do it with variables already, why not also with functions? Yes it could be abused but so can mutable variables and const_cast.


I'm sure there's other examples of this problem occurring, but I figured this is a pretty easy to understand real-world example.