Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reasoning behind "enumeral and non-enumeral type in conditional expression"

Since the C++11 transition GCC outputs a warning "enumeral and non-enumeral type in conditional expression". I'd like to understand the reasoning behind this warning. What are the perils of comparing enum constants?

It is obvious we can get rid of this warning by

  • -Wno-enum-compare
  • by explicitly casting to an integral type

But why the hassle? Personally, I always strive at writing warning free code, and usually the warnings emitted by default are quite sensible. For example, it figures that it's dangerous to compare signed and unsigned integers.

But using enums is widely used idiomatic C++ metaprogramming. I am not aware of any alternative, which is similarly readable, concise and to the point and does not require any actual storage.

To quote a concrete example: What can go wrong with the following metafunction, so that a warning would be adequate?

template<class TYPES>
struct MaxSize;
template<>
struct MaxSize<NullType>
  {
    enum{ value = 0 };
  };
template<class TY, class TYPES>
struct MaxSize<Node<TY,TYPES> >
  {
    enum{ thisval = sizeof(TY)
        , nextval = MaxSize<TYPES>::value
        , value   = nextval > thisval?  nextval:thisval
        };
  };
like image 497
Ichthyo Avatar asked Apr 16 '15 20:04

Ichthyo


2 Answers

It looks like an issue in GCC. This doesn't depend on -std=c++11 option. The warning is related not to comparison, but to the conditional operator.

Seems that the warning is issued only if one of enum members is iniatialized with an unsigned value, and another one is initialized with the value of another enum. Also, the warning is issued only in the initialization of enum members.

I managed to strip down the code to the following:

enum A { valueA = 1 };

enum B { 
    // if you change 0u to 0, there'll be no warning
    thisval = 0u, 
    // if you change valueA to any integral constant, there'll be no warning
    nextval = valueA, 
    // warning on this line
    value = nextval > thisval ? nextval : thisval,
    // no warning here
    value2 = nextval > thisval
};

int main() {
    // no warning here
    (void)(nextval > thisval ? nextval : thisval);
    // the same warning also here - but not with Clang
    (void)(nextval > thisval ? nextval : false);
}

Online demo

Clang doesn't issue any warnings on this code (except that with -Weverything it complains about unreachable code), though it could really say something on the last line.

(GCC 4.9.2, Clang 3.5.0)

like image 86
Anton Savin Avatar answered Nov 14 '22 14:11

Anton Savin


If you are using the enum as a way to define a compile time constant value , you can stop doing that now.

Use this instead:

constexpr int myConstant = 123;

where int can be anything that qualifies as a literal type [I won't try to define literal type here -- it's complicated and Google is your friend.]

Alternatively even before c++11 you could say:

class Foo{
    static const int myConstant = 123;
 };

where int can be any integral type.

There is no need to have the corresponding definition in a cpp file unless you need the address of myConstant somewhere.

The compiler is telling you that comparing an enum value to anything other than another value in the same enumeration is fragile code.

like image 44
Dale Wilson Avatar answered Nov 14 '22 13:11

Dale Wilson