Compiler warnings can sometimes help us find bugs before we even build our program, but it only works provided that we can make use of the warnings. Especially, when we are aware that warnings detect only symptoms of the bugs rather than bugs themselves.

By “bug” we mean the situation where our program compiles, and is a “correct” C++ program according to C++ Standard, but it does something else than what we intended. This could occur when we make a typo. Here is one instance:

int make_i(bool c1, bool c2) { int v; // no value specified if (c1) fill_1(v); if (!c1 && c2) fill_2(v); if (!c1 && c2) fill_3(v); // <- BUG return v; }

Functions fill_1() , fill_2() , fill_3() are used to compute and set the initial value to the object of type int that is passed by reference. This interface where a to-be-initialized object is passed by reference is very unfortunate, but suppose this is an interface of a third party library that we have to use, and in fact we are writing a wrapper around it, so that in our program we will just use make_i() that simply returns the computed value.

But I have made a typo: the three conditions were supposed to cover all possible combinations of c1 and c2 , but I forgot to type a bang before c2 in line 6. However, this is still a correct program, according to the rules of C++, and my compiler will accept it. But apart from compilation we can run a static analyzer which will take far more time than the compiler, but in turn will perform a deeper analysis of the program flow. I am using Clang Static Analyzer embedded in clang.

A static analyzer is not clever enough to be able to detect that I wrote something else than what I intended: it does not know my intentions. But it can look for things that are so fishy that they can hardly be considered a programmer’s intention. One such thing is returning an object of type int (or similar) with unspecified value. And it so happens that as a result of my bug I ended up returning an unspecified value. One of the checks in the static analyzer, called core.uninitialized.UndefReturn , looks for exactly this type of fishy situations. It reports a warning. It does not know that I have a bug, but it does see a fishy situation. When I see this report, I have sufficient information to find the bug myself. The important distinction here is: it is me who found the bug (missing bang operator), not the static analyzer. The static analyzer only found that an unspecified value is returned, which is something different than a missing bang. But the return of an unspecified value is a symptom of a bug. Without this symptom, I would not be able to see that there is a bug in function make_i() that I should look for.

This reasoning may sound obvious, but some programmers get the idea wrong. When they see a warning in function make_i() saying that v is potentially returned containing an unspecified value, they immediately “fix” the problem by changing the function to:

int make_i(bool c1, bool c2) { int v = 0; if (c1) fill_1(v); if (!c1 && c2) fill_2(v); if (!c1 && c2) fill_3(v); return v; }

And they are confident that they made the program better because there was a warning reported before, and now there is no warning reported. What they do not realize that they have only removed the symptom, but its cause — the bug — is still there, except that now there is nothing to warn us about it.

This idea to assign the initial value did not come out of nowhere. This is a misunderstood advice, which is otherwise good, that you should assign meaningful values to your objects upon initialization. But this advice is talking about meaningful values. And because of the constraints of the interface of functions fill_1() , fill_2() and fill_3() , we do not have a meaningful value to start with, so using some arbitrary value 0 which we will overwrite anyway does no good to anyone. And this situation is not uncommon. Consider reading value from a stream:

int i; std::cin >> i;

Interestingly, in other languages that do assign deterministic values to objects of type int upon initialization, there is no unspecified value, and a warning about a bug like the one above cannot be produced. These languages do the same thing as the described unaware programmer. This is why I value C++ over many other languages: that it offers more opportunities to catch bugs statically without even running the program or even running unit tests. This is worth considering: unspecified values can help detect bugs.