How do I make 0 auto cast to an enum? (|= works but not =)

Viewed 115

If you take a look at the code below you can see I overloaded |= to allow ORing with ints. However I can't figure out how to overload =. Clang seems to say I need = to be in the enum body AND that the enum body can't have overloads. So I'm confused and it seems impossible?

I mostly want 0 to be an acceptable value of Test I don't actually need all ints to be casted but it's fine if they are (and maybe I will later)

I'm using C++ 20

#include <cstdio>

enum Test {
    Test_NA,
    one, two, three
};

inline Test& operator |= (Test&a, int b) { return a =(Test)(a|b); }

int mytest(Test v) {
    return v == Test_NA ? 11 : v == three ? 33 : v;
}

int main() {
    Test val = two;
    printf("%d\n", mytest(val));
    val|=1;
    printf("%d\n", mytest(val));
    printf("%d\n", mytest(one));
    printf("%d\n", mytest(0)); //Error here
}
3 Answers

How do I make 0 auto cast to an enum? (|= works but not =)

You cannot make integers implicitly convertible to enums, nor can you define non-compound assignment operator for non-classes such as enums. I am actually a bit surprised that compound assignment is overloadable for enums.

So I'm confused and it seems impossible?

Let me clear your confusion: It is as it seems; what you're trying is impossible.

You can use an explicit conversion instead:

mytest(static_cast<Test>(0))

You cannot do what you want to do.

You can implement a user defined literal.

constexpr Test operator""_Test(int x){return (Test)x;}

now 0_Test is a literal of type Test.

With a bit more work, you can make this fail to compile if it is out of range. (Use the variardic template literal operator; that allows you to parse the integer at compile time guaranteed, and fail at compile time).

template <char... Cs> constexpr Test operator ""_Test() {
  static_assert(sizeof...(Cs) == 1);
  constexpr auto result = 0 + ... + (Cs-'0');
  static_assert( result >= 0 && result <= 3 );
  return (Test)result;
}

now 0_Test, 1_Test, 2_Test and 3_Test are literals that translate to the 4 possible enum values. Anything else just gives a compile time error.

The reason for the perhaps misleading error message content is:

[over.ass/1]: A simple assignment operator function is a binary operator function named operator=. A simple assignment operator function shall be a non-static member function.

This is also why there's no workaround: an enum (even a scoped enum) is not a class, so it cannot have a non-static member function. You're stuck not being able to do this. And, actually, that's okay: you should treat an enum like an enum, not like an int. If you want an int, use an int.

There is no such rule for |=, so you're free to make a nice non-member overload for that operator.

Related