Remember the if(NULL == p)
pattern ?
There are a lot of people who will tell a "you must write code like this":
if(NULL == myPointer) { /* etc. */ }
instead of
if(myPointer == NULL) { /* etc. */ }
The rationale is that the first version will protect the coder from code typos like replacing "==" with "=" (because it is forbidden to assign a value to a constant value).
The following can then be considered an extension of this limited if(NULL == p)
pattern:
Why const-ing params can be useful for the coder
No matter the type, "const" is a qualifier that I add to say to the compiler that "I don't expect the value to change, so send me a compiler error message should I lie".
For example, this kind of code will show when the compiler can help me:
void bar_const(const int & param) ;
void bar_non_const(int & param) ;
void foo(const int param)
{
const int value = getValue() ;
if(param == 25) { /* Etc. */ } // Ok
if(value == 25) { /* Etc. */ } // Ok
if(param = 25) { /* Etc. */ } // COMPILE ERROR
if(value = 25) { /* Etc. */ } // COMPILE ERROR
bar_const(param) ; // Ok
bar_const(value) ; // Ok
bar_non_const(param) ; // COMPILE ERROR
bar_non_const(value) ; // COMPILE ERROR
// Here, I expect to continue to use "param" and "value" with
// their original values, so having some random code or error
// change it would be a runtime error...
}
In those cases, which can happen either by code typo or some mistake in function call, will be caught by the compiler, which is a good thing.
Why it is not important for the user
It happens that:
void foo(const int param) ;
and:
void foo(int param) ;
have the same signature.
This is a good thing, because, if the function implementer decides a parameter is considered const inside the function, the user should not, and does not need to know it.
This explains why my functions declarations to the users omit the const:
void bar(int param, const char * p) ;
to keep the declaration as clear as possible, while my function definition adds it as much as possible:
void bar(const int param, const char * const p)
{
// etc.
}
to make my code as robust as possible.
Why in the real world, it could break
I was bitten by my pattern, though.
On some broken compiler that will remain anonymous (whose name starts with "Sol" and ends with "aris CC"), the two signatures above can be considered as different (depending on context), and thus, the runtime link will perhaps fail.
As the project was compiled on a Unix platforms too (Linux and Solaris), on those platforms, undefined symbols were left to be resolved at execution, which provoked a runtime error in the middle of the execution of the process.
So, because I had to support the said compiler, I ended polluting even my headers with consted prototypes.
But I still nevertheless consider this pattern of adding const in the function definition a good one.
Note: Sun Microsystems even had the balls to hide their broken mangling with an "it is evil pattern anyway so you should not use it" declaration. see http://docs.sun.com/source/817-6698/Ch1.Intro.html#71468
One last note
It must be noted that Bjarne Stroustrup seems to be have been opposed to considering void foo(int)
the same prototype as void foo(const int)
:
Not every feature accepted is in my opinion an improvement, though. For example, [...] the rule that void f(T) and void f(const T) denote the same function (proposed by Tom
Plum for C compatibility reasons) [have] the dubious distinction of having been voted into C++ “over my dead body”.
Source: Bjarne Stroustrup
Evolving a language in and for the real world: C++ 1991-2006, 5. Language Features: 1991-1998, p21.
http://www2.research.att.com/~bs/hopl-almost-final.pdf
This is amusing to consider Herb Sutter offers the opposite viewpoint:
Guideline: Avoid const pass-by-value parameters in function declarations. Still make the parameter const in the same function's definition if it won't be modified.
Source: Herb Sutter
Exceptional C++, Item 43: Const-Correctness, p177-178.