In this post I outline different views on the C++ language keyword “const” and give some hints on how you can use this keyword to improve the overall quality and the maintainability of your code base.

Should we Bother with Const?

In the C++ language const is a totally optional keyword. Let me explain what I mean here: When you have a fresh start on a new code base you can choose to completely ignore const and ban any usage of that keyword; and in fact some programmers I know are exactly doing that. If they never declare any variable, parameter or field const, they also don’t have to worry about declaring any methods const and their compiler will never complain about any incorrect usage of const related things.

So why should we bother with const at all? On the other hand side, const is part of the C++ language and there needs to be a reason for that. To find out, let’s take a quick look at what const actually does.

If you would use const to qualify the type of some variable, field or parameter that you declare, it would mean that you will not be allowed to modify that variable after its initialization.

const int foo = 5;      // OK: initialization of const variable is allowed
int bar = foo;          // OK: assignment does not modify const variable
foo = 0;                // ERROR: value of const variable cannot be modified

This mechanism seems to be easily enforcible for built-in types because the compiler knows which methods or operators will modify a variable and which won’t. But how do we tell the compiler about this when writing our own methods? For this reason the const qualifier can also be applied to methods. It will tell the compiler that it’s safe to call a specific method on a const qualified variable and it will also enforce this statement by not allowing you to modify this and any of its members from within the scope of such a const qualified method.

int Bar::getFoo() const
{
    m_callCount++;      // ERROR: member variable cannot be modified
    return m_foo;       // OK: member variable won't be modified here
}

And that’s almost all about it. Sounds quite simple, doesn’t it? But there is a catch to it. Keep in mind that once you start declaring const variables of your custom types, you also have to care about declaring const methods for these types. Otherwise those variables would be pretty useless.

class Foo
{
public:
    int bar();
};

int getBar()
{
    const Foo foo;
    return foo.bar();    // ERROR: non-const method be called on const variable
}

So if you think this through, it sounds much like an infestation of your code base where const spreads further and further. This is where a lot of programmers stop and say:

“Ok, let’s just omit const completely so we have the freedom to modify everything, everywhere. If we would write our code using const and then suddenly we want to call a non-const method on a const function parameter, we’d potentially have to re-write a lot of code leading to point where the parameter is passed into our function. Or we would have to magically cast the const away. But that already sounds really hacky…”

So what should be clear after reading this purely fictional statement: On the one hand side const is not helping you write code. But on the other hand, programming is not only writing code. It’s also reading and figuring out code that other people (including your former self) have written; and solving problems; and expressing the solutions to these problems so that other people (including your future self) can comprehend what you did. And if you read code it really helps a great deal to know if the value of a variable is allowed to change throughout the scope of a function without having to read through that entire scope; or if a method will change the state of the object it is called upon, just by looking at the method’s signature.

And when looking at it this way, const turns from being the enemy that restricts your by yelling “No, you shall not modify this variable!” into a friend that promises “Don’t you worry, that value will always stay the same.”

So when writing code you can usually decide whether or not a method should modify the object it is called upon before actually writing that method. Or if a parameter that gets passed into your function shall be modifiable by that method. And if you start expressing these decisions by properly using the const keyword the compiler will actually help you enforce your decision! If you accidentally modify a const variable it will tell you “Hey, wait a second, someone meant for this pointer that gets passed into this function not to change. Maybe you should think about what you are doing there.”

int foo(const int* bar)
{
    bar[0] += 4;        // ERROR: const variable cannot be modified
    return bar[0];      // OK: member variable won't be modified here
}

Ok, so using const properly lets you see the violation of your decisions and assumptions at compile time. This sounds pretty good - better than writing something that reads int special value = 2; // this value will never be modified!, because no one is forced to adhere to that comment.

Hints for Using Const

If you now decide that using const is the right way to go, you may run into nasty situations sometimes. Here some examples of such issues and what’s the common practice to resolve them:

Let’s assume that you are writing a const getter for some value that is computationally intensive to derive. So you decide to cache the result inside the getter for the next time you are calling this getter:

class Foo
{
public:
    int getValue() const
    {
        if (m_cachedValue == -1)
        {
            m_cachedValue = heavyCalculations();    // ERROR: member variable
                                                    // cannot be modified in
                                                    // const method
        }
        return m_cachedValue;
    }

private:
    int m_cachedValue;
};

In this case the const getter is not allowed to modify the m_cachedValue member. To get around this restriction the C++ language offers the mutable keyword. This keyword allows you to bend the strict rules of const by telling the compiler that the qualified member variable is allowed to be modified even inside the scope of a const method. So the example from above can be solved like this:

class Foo
{
    // ...
private:
    mutable int m_cachedValue;
};

For the next example let’s take a look at the following code snippet:

class Bar;

class Foo
{
public:
    Bar* getBar() const
    {
        return m_bar;
    }

private:
    Bar* m_bar;
};

Even though this code will compile without an error, it contains a thorough logical inconsistency: the method getBar() is declared const which means that it can be called on a const variable, that means it can be called on a variable that you wouldn’t expect to change. However, the getBar() method returns a pointer to some member of the const variable that in fact can me modified. And by modifying this member you would also modify the const variable, like this:

void main()
{
    const Foo foo;
    Bar* bar = foo.getBar();
    bar->setValue(5);   // setting the value of bar also changes the state of
                        // foo, even though foo is const!
}

To avoid this issue there is a simple trick that you can apply. In C++ it is allowed to copy the value of a non-const variable into a const variable at initialization, but not vice versa. That means that const is simple to add but hard to remove. So, even though you member Bar* m_bar is not const this doesn’t mean that you always have to use it as non-const variable. Thus, your const getter can just return a const Bar*, which fixes the logical issues you had before, because now calling bar->setValue(5) would not compile anymore and the promise that foo will always stay the same is kept.

You may already have seen people maintaining two different getters for such member pointers:

Bar* getBar();
const Bar* getBar() const;

The question is not whether this is a good or a bad thing to write, or whether you like this style or you don’t. C++ is a very explicit language where you can write down exactly what you want to achieve. And here the goal was to maintain the const correctness for two different cases: If the getBar() is called on a non-const variable, the first getter will be invoked and the result is also non-const, thus it can be modified. But if getBar() is called on a const variable, the second getter will be invoked and the result is also const.

Using const in a consistent manner can really benefit a code base. It makes the code more readable and more secure by preventing unintended use of your variables. If you now think “I’d really like to do that, but const is hard to introduce to my existing code base because needs changes everywhere”, keep in mind that const can be introduced gradually. You can start by declaring methods const because that won’t break anything in the rest of your code base. Once you have looked at all the methods of a certain type, you can look at usages of that type and decide whether or not to apply const.

What I found is that this is a great thing to do when reading an unfamiliar (part of a) code base: I mean, it’s fine to look at the code to understand it, but as we all know, writing or refactoring code provides a much deeper understanding! So one thing I like to do in preparation of working with unfamiliar code is to add const wherever I think it is appropriate because I can express my hypothesis about the source code and the compiler will complain if I missed something. And when I’m done with all the “reading” the outcome is not only that I understand the code, but I also improved the quality of that code!

East Const, Const West

A last thing to mention that apparently was a big thing at this year’s C++Now conference is the debate between “East Const” and “Const West”. The C++ language allows you to place the const qualifier before or after the qualified type.

int const foo;    // East Const
const int foo;    // Const West

At first glance the Const West notation may be your preferred choice of writing code, because you can read the statement from the left to the right; but if you add const pointers to the debate you will see that East Const has advantages, too:

int const * const foo;    // East Const
const int * const foo;    // Const West

Both of these snippets define a constant pointer to an constant integer. So neither the value that is pointed to may change, nor the address that the pointer points to. If you write it the East Const way you can read the type from right to left and it will always make sense. Or as Jon Kalb puts it:

Notice that the rule for const placement is:

   const modifies what is on its left. Unless there is nothing on its left, in which case it modifies what’s on its right.

If you consistently place const after what it modifies, the rule becomes much simpler:

   const modifies what is on its left.

Here are some more examples for the East Const notation:

int * const foo;        // const pointer to an int
int const * const foo;  // const pointer to a const int
int const * foo;        // pointer to a const int

For this post I won’t add an argument for preferring the one style over the other, so you can make up your own mind on where to put the const. What matters more is to answer the question of the headline: “Should we treat const as friend or foe when writing C++ code?” After writing this post my answer would be: “Always use const, except when writing throw-away prototyping code.” But in reality, when do we really throw away our prototyping code? So to keep things simple, my answer will become a plain “Const is your friend. Always use const!”

Sourcetrail 2018.4

Big indexing performance improvements, reduced memory consumption, new tabs bar to open multiple symbols simultaneously and more type-use edges for templates/generics. Continue reading

Sourcetrail 2018.3

Published on August 06, 2018

Sourcetrail 2018.2

Published on April 23, 2018