tl;dr: C++ functions do not require a return keyword in them to be well-formed and compile, though it will likely cause undefined behaviour. Your specific compiler may also warn or error. In almost all cases, you want a return keyword.
C++, a language notorious for its ability to fall over and fail to compile at the slightest of mistakes, has what appears at first glance to be a huge oversight in the specification: a non-void function does not require a return keyword to produce compilable code.
Compiling and running this through GCC 4.6.3 provides the following output:
Clearly it’s noticed there’s an issue and shown a warning, but has gone ahead anyway and created a runnable executable.
So why is this the case? And what knock-on effects does this have?
Simply put, nowhere in the C++ specification does it say a function must have a
return keyword to produce code that compiles. However, it does say that any function with a non-void return type, that reaches the last line (where your closing brace would be), will produce undefined behavior.
Undefined behavior could be anything — the program could crash, it could return garbage values (this is most likely!), the program could even attempt a coup and try to overthrow the local government by force. It’s all up to your compiler to do as it wishes.
We’ll take a look at the specific wording later, but for now let’s consider the impact this has on compilers.
Because not all functions necessarily return.
function5 never returns, strictly speaking. Instead, it throws an exception, the stack is unwound and we travel upwards to find a function that can catch. The
return 1; is never reached.
Similarly, function6 exits the program before the
This all seems dumb. Why doesn’t then compiler just check for those cases? It’s not that hard to ensure all functions return, or throw, or exit right?
Can you guarantee function7 will ever return? All it does is wait for a user to type some input. What if the user never touches their keyboard? How would the compiler know?
What about function8? We, as programmers, know that
rand() will never return a number larger than 1 — but the compiler may not know that, especially if rand is included from some unknown, external library.
This is related to the halting problem, an age old computer science question. It asks, in a very simplified form:
To save you some 200 years of computer science proofs, the answer is, no, you cannot.
As a result of it, if the C++ spec guaranteed all functions must have valid return keywords in all possible paths for all functions, it would be impossible to implement. No matter how many edge cases you try to patch, the halting problem guarantees there will always be some function you cannot compute the full set of return paths for.
By leaving it as undefined behaviour, different compilers can perform sensible (but not necessarily exhaustive of all functions) analysis, and warn / error where they believe
return keywords should exist.
We’ll get a bit more technical here, however there’s nothing here we haven’t already covered. As per [stmt.return]:
Don’t worry if this very specific and legal wording is off-putting — let’s go through it together.
The C++ specification defines the
return statement in this section, [stmt.return], and makes various references to it throughout the rest of the specification. However, nowhere in the specification does it say that a function requires a
return keyword in order to create well-formed code. Well-formed code is, in its simplest form, code that will successfully compile.
Surely this means it’s totally safe to ignore
return keywords then? No.
“Flowing off” the end of a constructor, destructor, or “cv void” returning function, is the same as putting a
return; on the last line. By cv void, it simply means a function with
void return type, that may be const or volatile. (This const / volatile distinction is not important.)
Flowing off means reaching the last line (where the closing curly bracket would be), where no more code follows.
As such, the following two functions are identical:
Note that the spec only defines implicitly adding the
return for void return functions, constructors, and destructors.
As for flowing off the end of a non-void function, this results in undefined behaviour. This means reaching the end of a function without having hit another jump statement to exit, where a jump statement may be one of
And this is completely true! As per [basic.start.main],
It is explicitly called out in the spec that not having a
return keyword in your
main function, will implicitly add a
There’s no specific coding or programming takeaway from this — I certainly wouldn’t recommend using this quirk in your code anywhere. Hopefully it encourages you to do more deep-diving into how C++ works, and you take the opportunity to take a look at the specification.
Let me know on Twitter if you found this interesting, if you have anything to add, or if you believe I’ve made any mistakes in my explanation of the halting problem!