1

In the code below, I use static_cast to convert an strongly typed enum to an int. The same works in the other direction. But it also works if the cast int is not within the range of the enum. Why is that and why is the compiler not catching this?

#include <iostream>
#include <string>

enum class Name {Hans, Peter, Georg}; // 0, 1, 2

std::string getName(Name name) {
    switch(name) {
        case Name::Hans:  return "Hans";
        case Name::Peter: return "Peter";
        case Name::Georg: return "Georg";
        default: return "not valid name";
    }
}

int main()
{
    // Cast a Name to an int, works fine.
    std::cout<< static_cast<int>( Name::Peter ) <<std::endl; // 1
    std::cout<< static_cast<int>( Name::Hans ) <<std::endl;  // 0

    // Cast an int to a Name
    std::cout<< getName(static_cast<Name>(2)) <<std::endl;   // Georg
    std::cout<< getName(static_cast<Name>(3)) <<std::endl;   // not a valid name
    // I would expect a compiler error/warning like i get here:
    // std::cout<< static_cast<int>( Name::Hans + 4 ) <<std::endl;
}
dani
  • 3,677
  • 4
  • 26
  • 60
  • 2
    On what basis do you think the compiler should "catch" this? As far as I know the C++ standard doesn't require any diagnostics for this. – nwp Jun 01 '17 at 13:26
  • Adding checks for validity of the integer value will impose a runtime overhead. – Bernard Jun 01 '17 at 13:33
  • The error I get when I uncomment that last line is `no match for ‘operator+’ (operand types are ‘Name’ and ‘int’)` which doesn't have anything to do with your question about enum range. – Kevin Jun 01 '17 at 13:33
  • I would expect that it is possible to pick this up, though, both clang and gcc don't emit a warning for this. As you are using `static_cast`, you already already telling the compiler: I know better! – JVApen Jun 01 '17 at 13:33
  • 3
    To cast means to overwriting the type system by providing additional information that it (the type system) can't or isn't allowed to deduce itself. The compiler will implicitly trust that this information is accurate, regardless of how implausible it is. Casts overwrite a safety system, they are not implicitly safe, the burden falls on the developer to use them correctly. – François Andrieux Jun 01 '17 at 13:43
  • This isn't the problem, but do you really need the extra stuff that `std::endl` does? `'\n'` ends a line. – Pete Becker Jun 01 '17 at 16:48

1 Answers1

0

For one thing, people often use enums to represent bit flags:

enum class FontFlags { bright=0x1, bold=0x2, blink=0x4 };

Now they expect this to work:

FontFlags(int(FontFlags::bold) | int(FontFlags::blink))

But of course the value is 6 which is "impossible."

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • Strongly typed enums (with `enum class`) do not support `operator|`. – Kevin Jun 01 '17 at 13:36
  • @Kevin: Updated to address that. – John Zwinck Jun 01 '17 at 13:37
  • I think that kind of defeats the purpose of this answer though. If you need to convert to an `int` before you can get an "impossible" value for the enum then you no longer have a valid enum. – Kevin Jun 01 '17 at 13:39
  • @Kevin: I'm trying to demonstrate why the compiler should not just reject construction or casting of enums from ints that aren't mapped. Whether I type `6` or `bold|blink` doesn't really matter - the value is the same, and it needs to be valid or people's code will break. – John Zwinck Jun 01 '17 at 13:42
  • @John Are you sure the standard mandates `6` to be valid when casted to `FontFlags`? I thought it is undefined behaviour to cast an unlisted value to the enum class. – Bernard Jun 01 '17 at 14:14
  • @Bernard: It's only UB if the value is outside the range of the `underlying_type` of the enum. So 6 does not lead to UB. See https://stackoverflow.com/questions/18195312/what-happens-if-you-static-cast-invalid-value-to-enum-class – John Zwinck Jun 01 '17 at 15:27