views:

288

answers:

6

When comparing enums that come from different sources such as those of the following code GCC emits warnings. Is there a way to avoid these warnings without c-style casts?

struct Enumerator
{
    enum { VALUE = 5 };
};

template<int V>
struct TemplatedEnumerator
{
    enum { VALUE = V };
};

if(Enumerator::VALUE == TemplatedEnumerator<5>::VALUE)
{
  ...
}

And GCC emits the following type of warning:

GCC: warning: comparison between 'enum ...' and 'enum ...'
+6  A: 

I believe you can provide your own comparison operators, but you'll have to name the enums first:

struct Enumerator
{
    enum Values { VALUE = 5 };
};

template<int V>
struct TemplatedEnumerator
{
    enum Values { VALUE = V };
};


template <int V>
bool operator==(Enumerator::Values lhs, typename TemplatedEnumerator<V>::Values rhs)
{
    return static_cast<int>(lhs) == static_cast<int>(rhs);
}

template <int V>
bool operator==(typename TemplatedEnumerator<V>::Values rhs, Enumerator::Values lhs)
{
    return static_cast<int>(lhs) == static_cast<int>(rhs);
}

Interestingly Visual Studio doesn't actually warn about comparing the two enumerator types, but rather warns about a constant value in a if statement - just like it will if you do this:

if (true) {...}
Eclipse
I'd be very hesitant about doing this. Comparing enums of different kinds shouldn't be something that "just works". While there is occasionally a need for it I believe making it explicit in the code is preferable. You can still wrap it in a helper function, but overloading operators seems wrong.
Phil Nash
If these enums exist only for the purpose of comparing them (common in template metaprogramming), then allowing them to be compared seems like a reasonable thing to do. Note that these operators will not affect the behavior of any other enums.
Eclipse
Thanks for your help Josh, but Konrad Rudolph's solution seems to be more updated, hadn't realized that would actually work, but your assumptions were all right.
Robert Gould
+1  A: 
#include <iostream>
using namespace std;

struct Enumerator 
{
  enum {value = 5 };
};


template<int v>
struct TemplatedEnumerator
{ 
  enum {value = v};
};



int main(void)
{
  if (static_cast<int>(Enumerator::value) ==
    static_cast<int>(TemplatedEnumerator<5>::value))
    cout << "Yoh\n";
}
Friedrich
+2  A: 

If there are N enums, won't there need to be N * (N - 1) comparison operators? That could end up being a lot.

Couldn't you just use the implicit conversion of enums to ints and do:

bool equals(int lhs, int rhs)
{
  return lhs == rhs;
}

Then you can just do this in your code:

if(equals(Enumerator::VALUE, TemplatedEnumerator<5>::VALUE))
{
  ...
}

Also, you can check out enum-int casting: operator or function. The accepted answer says that you shouldn't use the implicit conversion, but a correctly named function like equals should make the code very readable and maintainable.

ZaDDaZ
you're right about the amount of code, but with full optimization they will all melt away, so there won't be as much overhead
Robert Gould
"If there are N enums" - then I would consider it a code smell. Even when N==1 I would check the design first - but these things to become necessary from time to time. The rest of your solution is ok - but "equals" might be too generic a name for this. What about "CompareDifferentEnums"?
Phil Nash
I agree with the necessity question. I assumed that was already considered when asking the question however and hopefully offered the easiest solution. I am not sure about the name though, this function would compary any to ints, not just enums.
ZaDDaZ
+2  A: 

Don't do it.

The warning is there because you shouldn't mix up different enums in the first place.

For constants you can use a const declaration.

starblue
This is for meta programming I need the enums , probably to advanced for your level yet but it is a very common pattern in C++ to generate templates code.
Robert Gould
Nah, the advice is justfied, you don't need enums here.
Konrad Rudolph
A: 

You're missing the obvious answer. No, don't use c-style cast. Use C++-style casts:

if(static_cast<int>(Enumerator::VALUE) == static_cast<int>(TemplatedEnumerator<5>::VALUE))
{

}

Perfectly safe, perfectly acceptable. Two enums are not the same types, so if there's anything fishy in your code, it's not the cast, it's the attempt to compare two distinct types. But as long as you do it, a cast would be my preferred way to do it. Yes, you could define a comparison operator, but a cast makes it clear that you're comparing two technically unrelated types.

jalf
+1  A: 

Simple answer in your case: don't use an enum, use an inline-defined static const int:

struct Enumerator
{
    static int const VALUE = 5;
};

template<int V>
struct TemplatedEnumerator
{
    static int const VALUE = V;
};

In this special case, that's equivalent and all compilers of the last few years should treat it so (I know for a fact that all the major ones do).

See also: static const Member Value vs. Member enum : Which Method is Better & Why?

Konrad Rudolph
will need to check it out, maybe this will work, but these enumerators are doing some very serious work so I can't say yet
Robert Gould
Tried the replacement and it worked, thanks! I had been carrying the enum hack in my bag of tricks from the days when most compilers didn't get the static const thing right!
Robert Gould