Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

switch may fall through (no it may not)

On GCC 7 I have enabled most of all warnings on Qt creator 4.9. Now I have a switch statement which covers all enumeration values. If I add a default: I get a warning (from Qt creator):

warning: default label in switch which covers all enumeration values

If I remove the default: I get another warning (from GCC):

error: this statement may fall through [-Werror=implicit-fallthrough=]  
       }  
       ^

error: all warnings being treated as errors

What am I supposed to do? Turn off warnings? They are useful, I don't want to turn off any of them, but Wimplicit-fallthrough seems to be faulty.

[[fallthrough]] doesn't help because the cases end with a return thus I get (from Qt creator):

warning: fallthrough annotation in unreachable code

__attribute__ ((fallthrough)) didn't do anything either. Neither did /* FALLTHRU */ or [[gnu::fallthrough]] or // fall through. Presumably because of -pedantic?

Example:

enum class E {a, b, c};

QVariant fun(E e) {
     switch (e) {
        case E::a: return "something";
        case E::b: return "something_else";
        case E::c: return "something_different";
        // default: return QVariant{};
        // Do I add a default:? What do I add here?
    }
}

Hopefully the things I've tried shows that my question is not a duplicate of this or this or other similar questions because they don't solve my problem.

like image 656
Ayxan Haqverdili Avatar asked Aug 16 '19 23:08

Ayxan Haqverdili


2 Answers

Consider fun(static_cast<E>(42)). This is a perfectly well-defined conversion, but the function from your question will reach the end without returning, and your program's behavior will be undefined. That's why GCC warns that control may reach the end of a non-void function.

So why not add a default case? Consider what happens if someone goes back and adds another constant to E, but forgets to update fun. Without a default, GCC will helpfully warn you that the switch doesn't handle all of E's constants. If you add a default case, you've defeated that very helpful protection.

So what's the Right ThingTM to do? Return your default value (or throw or call abort() to crash, as appropriate) at the end of the function, after the switch:

enum class E {a, b, c};

QVariant fun(E e) {
     switch (e) {
        case E::a: return "something";
        case E::b: return "something_else";
        case E::c: return "something_different";
    }
    return "some_default"; // or throw or abort()
}

This gives you the best of both worlds. If someone passes a value that isn't one of the pre-defined enumerator constants, it will behave in a well-defined way. If someone adds a new constant to E and forgets to update fun then the compiler will still issue a warning.

For further discussion, Jason Turner covered this topic (among others) in his CppCon 2018 talk, which is worth a watch.

like image 184
Miles Budnek Avatar answered Nov 09 '22 22:11

Miles Budnek


If my first answer doesn't satisfy, this perhaps this will. This is how I resolved the issue locally:

QVariant fun(E e) {
    switch (e) {
        case a: return "something";
        case b: return "something_else";
        case c: return "something_different";
    }
    return "";
}
like image 45
selbie Avatar answered Nov 09 '22 23:11

selbie