In the following C++ code:
typedef enum { a, b, c } Test;
int foo(Test test) {
switch (test) {
case a: return 0;
case b: return 1;
case c: return 0;
}
}
a warning is issued when compiling with -Wall
, saying that control reaches end of non-void function. Why?
Its not generally correct to say that the variable test
in the example can contain any value.
foo(12354)
does not compile:
> test.cpp:15:14: error: invalid conversion from ‘int’ to ‘Test’ > test.cpp:15:14: error: initializing argument 1 of ‘int foo(Test)’
because 12354 isn't a valid Test
value (though it indeed would be valid in plain C, but it's not in C++).
You sure could explicitly cast an arbitrary integer constant to the enum type, but isn't that considered Undefined Behaviour?
The problem is that a variable of type Test
can have any value allowed by the type the compiler gives it. So if it decided it was a 32-bit unsigned integer, any value in that range is allowed. So, if for instance you call foo(123456)
, your switch
statement will not catch any value and there's no return
after your switch
.
Put a default
case in your switch or add some error-handling code.
Although there is actual danger of passing foo
an argument that will not hit any of the return
statements, the warning does not depend on enum, or on the danger. You can see the same effect with bool
, in a switch statement which is (as far as I can tell) completely watertight.
In general the compiler isn't smart enough to deduce whether you've covered every possible path that control could actually take through the switch
statement. To be that smart it would have to be able to deduce all of the possible states the program can reach before entering the switch
, which leads straight to the Halting Problem.
So the deduction has to stop somewhere, and (at least with gcc) it stops with the determination that there is no default case and that therefore control might be able to leave the switch
without hitting return
.
There is no guarantee that the variable test
will contain a valid enum so it is in fact possible for you to reach the end of your non-void function, e.g. if your calling code looks like this:
Test test = Test(3);
foo(test);
Although your enum
has only three declared states, the implementation will actually choose a larger integral type (typically int) to store the values, which are NOT restricted to those declared. The Standard just makes some minimal guarantees to ensure it can at least handle the values specified and certain combinations. Sometimes this freedom is essential, as the enum
values are intended to be bitwise-ORed together to form combinations. So, the function foo
can actually be called with say, Test(3)
, which wouldn't find a return
statement in your switch
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With