What is the point of noreturn?
The noreturn attribute is supposed to be used for functions that don't return to the caller. That doesn't mean void functions (which do return to the caller - they just don't return a value), but functions where the control flow will not return to the calling function after the function finishes (e.g. functions that exit the application, loop forever or throw exceptions as in your example).
This can be used by compilers to make some optimizations and generate better warnings. For example if f
has the noreturn attribute, the compiler could warn you about g()
being dead code when you write f(); g();
. Similarly the compiler will know not to warn you about missing return statements after calls to f()
.
How can a [[noreturn]] function have a return type?
The [[noreturn]]
is an attribute which has whatever semantics it has. It doesn't change, however, how the function is declared: all normal functions in C++ (i.e., all functions except the constructors, destructors, and conversion operators) have a declared return type. Adding any attribute doesn't change this rule.
The purpose of the [[noreturn]]
attribute is probably to indicate that the function never returns in a normal way. Given that the function is also declared to be noexcept
it basically means that the corresponding function also can't thrown an exception. One example of a function with similar behavior is exit()
which terminates the program. I could imagine that functions implementing some sort of application-loop could also qualify. In any case, the [[noreturn]]
tells the system that the corresponding function will never normally return, i.e., falling off the function ("after }") will probably result in undefined behavior.
Why does the main function work with no return value?
Normally it is not allowed for the control flow to reach the end of a non-void function without returning something. The main
function is handled differently, as specified in the standard.
From http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2960.pdf:
§ 3.6.1/5
If control reaches the end of main without encountering a return
statement, the effect is that of executing return 0;
As for the rationale, I'm not sure, honestly. If someone knows, please add it to my answer or as a comment.
Is this a valid use of the noreturn attribute?
I'd say that it is valid but pointless.
It's valid because the function does not return. The contract cannot be broken.
It's pointless because the function is never called from C++ code. So no caller can make use of the fact that the function does not return because there is no caller. And at the point of definition of the function, the compiler should not require your assistance to determine that code following the while
statement is dead, including a function postlude if any.
Why does noreturn function return?
The function specifiers in C are a hint to the compiler, the degree of acceptance is implementation defined.
First of all, _Noreturn
function specifier (or, noreturn
, using <stdnoreturn.h>
) is a hint to the compiler about a theoretical promise made by the programmer that this function will never return. Based on this promise, compiler can make certain decisions, perform some optimizations for the code generation.
IIRC, if a function specified with noreturn
function specifier eventually returns to its caller, either
- by using and explicit
return
statement - by reaching end of function body
the behaviour is undefined. You MUST NOT return from the function.
To make it clear, using noreturn
function specifier does not stop a function form returning to its caller. It is a promise made by the programmer to the compiler to allow it some more degree of freedom to generate optimized code.
Now, in case, you made a promise earlier and later, choose to violate this, the result is UB. Compilers are encouraged, but not required, to produce warnings when a _Noreturn
function appears to be capable of returning to its caller.
According to chapter §6.7.4, C11
, Paragraph 8
A function declared with a
_Noreturn
function specifier shall not return to its caller.
and, the paragraph 12, (Note the comments!!)
EXAMPLE 2
_Noreturn void f () {
abort(); // ok
}
_Noreturn void g (int i) { // causes undefined behavior if i <= 0
if (i > 0) abort();
}
For C++
, the behaviour is quite similar. Quoting from chapter §7.6.4, C++14
, paragraph 2 (emphasis mine)
If a function
f
is called wheref
was previously declared with thenoreturn
attribute andf
eventually
returns, the behavior is undefined. [ Note: The function may terminate by throwing an exception. —end
note ][ Note: Implementations are encouraged to issue a warning if a function marked
[[noreturn]]
might
return. —end note ]3 [ Example:
[[ noreturn ]] void f() {
throw "error"; // OK
}
[[ noreturn ]] void q(int i) { // behavior is undefined if called with an argument <= 0
if (i > 0)
throw "positive";
}
—end example ]
return, return None, and no return at all?
On the actual behavior, there is no difference. They all return None
and that's it. However, there is a time and place for all of these.
The following instructions are basically how the different methods should be used (or at least how I was taught they should be used), but they are not absolute rules so you can mix them up if you feel necessary to.
Using return None
This tells that the function is indeed meant to return a value for later use, and in this case it returns None
. This value None
can then be used elsewhere. return None
is never used if there are no other possible return values from the function.
In the following example, we return person
's mother
if the person
given is a human. If it's not a human, we return None
since the person
doesn't have a mother
(let's suppose it's not an animal or something).
def get_mother(person):
if is_human(person):
return person.mother
else:
return None
Using return
This is used for the same reason as break
in loops. The return value doesn't matter and you only want to exit the whole function. It's extremely useful in some places, even though you don't need it that often.
We've got 15 prisoners
and we know one of them has a knife. We loop through each prisoner
one by one to check if they have a knife. If we hit the person with a knife, we can just exit the function because we know there's only one knife and no reason the check rest of the prisoners
. If we don't find the prisoner
with a knife, we raise an alert. This could be done in many different ways and using return
is probably not even the best way, but it's just an example to show how to use return
for exiting a function.
def find_prisoner_with_knife(prisoners):
for prisoner in prisoners:
if "knife" in prisoner.items:
prisoner.move_to_inquisition()
return # no need to check rest of the prisoners nor raise an alert
raise_alert()
Note: You should never do var = find_prisoner_with_knife()
, since the return value is not meant to be caught.
Using no return
at all
This will also return None
, but that value is not meant to be used or caught. It simply means that the function ended successfully. It's basically the same as return
in void
functions in languages such as C++ or Java.
In the following example, we set person's mother's name and then the function exits after completing successfully.
def set_mother(person, mother):
if is_human(person):
person.mother = mother
Note: You should never do var = set_mother(my_person, my_mother)
, since the return value is not meant to be caught.
Why does defining main() with no return type compile with no error?
The program is ill-formed. Omitting the return type is not permitted by the C++ standard.
The reason the compiler doesn't treat it as a fatal error is historical. Prior to the 1999 standard, C did permit the return type of a function to be omitted; it would default to int
. C++ is derived from C, so early (pre-standard) versions of C++ had the same rule.
In modern C++, omitting the return type is an error. The compiler is required to diagnose such an error, but it's not required to treat it as fatal. By printing a warning, the compiler has done its job as far as the standard is concerned.
Don't ignore warnings.
Related Topics
Range-For-Loops and Std::Vector<Bool>
How to Compare Multiple Strings Inside an If Statement
Purpose of Trigraph Sequences in C++
Finding the Centroid of a Polygon
How to Enable _Int128 on Visual Studio
How Would You Implement Your Own Reader/Writer Lock in C++11
Is a Pointer with the Right Address and Type Still Always a Valid Pointer Since C++17
C++ How to Use Select to See If a Socket Has Closed
How to Force Linker to Use Shared Library Instead of Static Library
Create Window Without Title Bar
What Is the Performance Penalty of C++11 Thread_Local Variables in Gcc 4.8
Changing the Current Directory in Linux Using C++
Detecting If Computer Is Idle Based on Mouse and Keyboard Interactions