3

I have a problem putting method args to my class:

class A {
  public:
    enum Mode {ModeA, ModeB, ModeC};

    ... // other methods, constructor etc

    void setMode(Mode m) {
      mMode = m;
    }

  private:
    Mode mMode;
}

int main(int argc, char **argv) {
  A a;
  a.setMode(A::ModeA | A::ModeC );

  return 0;
}

The problem, I get a C++ compiler error invalid vconversion from int to A::Mode, I dont understand, why I can't concat to enum values? I need to concat values in my code, so any help to solve this would be very nice.

dandan78
  • 13,328
  • 13
  • 64
  • 78
Andreas
  • 1,617
  • 15
  • 18
  • 1
    Enums in C++ areń't supposed to be concatted, because the result isn't any more of type enum. – Ashalynd Oct 12 '13 at 17:30
  • 1
    @Ashalynd I agree, all the answers seem to miss the point that this operation will actually lead to information loss. If `ModeA==0` (which it should by default) then there's no way to distinguish `ModeC` from `ModeA | ModeC`. I wouldn't recommend using an enum to store flags. You may manually list the individual flag names in an enum (manually assigning 0x1,0x2,0x4,0x8,etc) but storing combinations should be done in a bitfield, the underlying type or something similar. – PeterT Oct 12 '13 at 17:53
  • 3
    Enums in C++ are supporting (and I daresay explicitly made safe to support) bit flags, see [this question](http://stackoverflow.com/q/18195312/420683) – dyp Oct 12 '13 at 18:05
  • @DyP that's only if the enum values are manually set to not collide. I just think it's weird to have a member of type `Mode` that may actually have a value that is not equal to any of the individual enum values but I see your point that the standard does not actually make a distinction between storing combinations in the underlying type or the enum type. Point taken. – PeterT Oct 12 '13 at 18:22
  • @PeterT I agree `enum` is not perfect to do this, but a scoped enum is more typesafe than `std::bitset` or the underlying type. (A better solution might be to wrap `std::bitset` in an opaque typedef, but this is more work, too.) – dyp Oct 12 '13 at 18:28

3 Answers3

12

The result of operator| for two enums is not an enum by default. After your class, add something like this:

A::Mode operator|( A::Mode a, A::Mode b )
{
    return A::Mode( int( a ) | int( b ) );
}

if your standard library supports it, the following is more future proof as the conversion to int is not always correct:

A::Mode operator|( A::Mode a, A::Mode b )
{
    typedef std::underlying_type< A::Mode >::type UL;
    return A::Mode( static_cast< UL >( a ) | static_cast< UL >( b ) );
}

Unlike the other answer, you just add this once (to the right place) and all uses are automatically covered.

Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • That was quick ;) For unscoped enums, I'd just use `return A::Mode(+a | +b);` which should do The Right Thing™ (note: added a unary `+`) – dyp Oct 12 '13 at 17:32
  • @DyP - an explicit conversion **is** required; without it, the `a | b` inside the operator just calls the operator again. – Pete Becker Oct 12 '13 at 17:33
  • 1
    @DyP I already edited the answer before you wrote your comment :) Also: `return A::Mode(a|b);` would send the compiler into infinite recursion. – Daniel Frey Oct 12 '13 at 17:34
  • Hm, with the operator i get a new compiler error: "operator A::Mode takes only one argument" – Andreas Oct 12 '13 at 17:34
  • @PeteBecker Unary `+` does the trick via integral promotion (see updated comment). – dyp Oct 12 '13 at 17:35
  • @AndreasBf As I wrote, put it **after** the class, not inside it. (And after the class' closing `}`, the code you posted is missing a `;`). – Daniel Frey Oct 12 '13 at 17:37
  • Implemented it outside of my class and it works now, many thanks :) The code above is dummy code to illustrate my problem, the class i wrote is some more complex ;) – Andreas Oct 12 '13 at 17:52
2

May be you need this :

a.setMode( (A::Mode) (A::ModeA | A::ModeC ));

A::ModeA | A::ModeC makes an int so cast it to A::Mode again

P0W
  • 46,614
  • 9
  • 72
  • 119
1

Underlying type of your enum is in this case probably int and your compiler can not rely on the flag constructed using the | (bitwise or) being a valid value from this enum.

However, you know that the result will be a valid value from this enum, so you could do:

A::Mode newMode = (A::Mode) (A::ModeA | A::ModeC);
a.setMode(newMode);
LihO
  • 41,190
  • 11
  • 99
  • 167