Const Correctness Part 3

by Darren Collins
Sunday, 4 April 2004

In last week's article, I said that the declaration

const int *i;

is more common than

int const *i;

even though they mean the same thing. I advised readers not to use the second version, as it might be confusing to inexperienced programmers who later need to maintain your code.

CodeCraft reader Eric Nagler wrote to express the opposite point of view. I was a little surprised at first, but I read his arguments and followed up on his references to articles by Dan Saks, and now I agree with him! This article outlines some of the reasons to prefer the second declaration above, so you can make your own informed decision about which to adopt.

The basic idea here is that you can decipher the meaning of all the consts in a declaration by reading it right to left. Here are some examples to illustrate:

int i;                // i is an int
int *i;               // i is a pointer to an int
int &i;               // i is a reference to an int
int const *i;         // i is a pointer to a constant int
int const &i;         // i is a reference to a constant int
int * const i;        // i is a constant pointer to an int
int const * const i;  // i is a constant pointer to a
                      //        constant int

By using the 'int const * i' form of a declaration, all you have to remember is that the const keyword modifies the type or declaration to its left. In other words, just read the declaration from right to left.

This rule also applies to constant member functions - the const keyword modifies the declaration to its left. I'll talk about constant member functions in a future article.

int f() const;        // constant member function

While I was writing this article, Eric sent me another email to illustrate another situation where following the 'int const *i' convention helps. It has to do with templates, which I haven't written about before, so don't worry if you can't follow this example. Just trust me that it's true!

Eric gave the example of a template function declaration similar to this:

template <class T>
const T& greater(const T& x, const T& y)
{ return (x > y) ? x : y; }

If you wanted to specialise it to allow the user to call it with 'char*' and 'const char*' arguments, you'd probably first try replacing T with 'char*':

template <>
const char*& greater(const char*& x, const char*& y)
{ return strcmp(x, y) > 0 ? x : y }

This won't compile, though. One way to get around it is by using a typedef (which isn't very obvious):

typedef char* P;
template <>
const P& greater(const P& x, const P& y)
{ return strcmp(x, y) > 0 ? x : y }

If you followed the 'int const *i' convention for declaring constants, though, you'd have written the original template as:

template <class T>
T const &greater(T const &x, T const &y)
{ return (x > y) ? x : y; }

Then when you wrote the char* version by replacing T with 'char*', you'd have come up with valid code:

template <>
char* const &greater(char* const &x, char* const &y)
{ return strcmp(x, y) > 0 ? x : y }

This result is much more intuitive than the typedef hack we used previously.

Summary

Thanks to Eric, I've changed my mind and now prefer 'int const *i' to 'const int *i'. I really do believe using this form is a good habit to get into, even though it might seem strange at first.

I hope this article has given you enough information to make up your own mind. Just remember, whichever convention you choose make sure you stick to it. You'll cause all sorts of confusion to beginners otherwise!

I'd like to send a huge thanks to Eric Nagler for his feedback on last week's article. He showed me a great way to make my code easier to understand, and supplied me with plenty of information to support it. Thanks Eric!

 


Related Articles
Index
- Links - C/C++
- Code Layout Styles
- Const Correctness Part 1
- Const Correctness Part 2
- Const Correctness Part 3
- Const Correctness Part 4
- Const Correctness Part 5
- Const Correctness Part 6

This site Copyright 1999-2005 Darren Collins.