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