Reputation: 1490
What I'm trying to achieve: I'm trying to implement something like a Java enum (ie. enum that has some additional functionality). I came up with a solution using two classes, where one class represents a value and the other acts as an enumeration of possible values using static variables to represent each value. I want it to be a real replacement of enum, including the possibility to use the enum values in template instantiation. For this to work, an enum value needs to be a constant expression (constexpr). However, I'm not sure if I use the constexpr correctly.
Here is the code that I came up with:
class EnumType {
public:
enum Enum {val_A, val_B, val_C};
friend class EnumTypeList;
EnumType()
: m_ID(val_A), m_foo(0) {}
constexpr operator Enum() const {return m_ID;};
constexpr unsigned int getFoo() const {return m_foo;};
protected:
constexpr EnumType(const Enum v, const int foo)
: m_ID(v), m_foo(foo) {}
private:
Enum m_ID;
int m_foo;
};
class EnumTypeList {
public:
static constexpr EnumType A = EnumType(EnumType::val_A, 5);
static constexpr EnumType B = EnumType(EnumType::val_B, 4);
static constexpr EnumType C = EnumType(EnumType::val_C, 8);
};
The class EnumType
holds the information of each value and provides some additional functions (here it is storing the additional value m_foo that can be accessed using getFoo()
function). The enumeration itself is represented by the EnumTypeList
that contains static constexpr variables, where each variable represents a possible value of an enum. This code allows me to use the variables from EnumTypeList
in place of EnumType::Enum.
Even in templates like in the following code:
template <EnumType::Enum T>
class Test {
public:
void test() {
std::cout << "Enum is not A" << std::endl;
}
};
template <>
class Test<EnumType::val_A> {
public:
void test() {
std::cout << "Enum is A" << std::endl;
}
};
int main() {
Test<EnumTypeList::A> a;
a.test();
Test<EnumTypeList::B> b;
b.test();
// this shouldn't compile
/*EnumType x = EnumTypeList::C;
Test<x> c;
c.test();*/
}
This works as I would expect – I can use the values from EnumTypeList
in place of EnumType::Enum
in template instantiation as demonstrated above, but I can't do it with EnumType x = EnumTypeList::C;
While the code compiles correctly without any warning under gcc and clang, I'm not sure if I use the constexpr correctly. The thing is that while the EnumType
constructor and the conversion operator operator Enum()
are constexpr, they both access variables m_ID
and m_foo
that are not constant (because I need assignment operator).
Upvotes: 1
Views: 1476
Reputation: 171333
This is fine, members of literal types are allowed to be modifiable.
In order to use the type in a constant expression you must construct it with constant arguments, but that's OK because you do that;
static constexpr EnumType A = EnumType(EnumType::val_A, 5);
The object constructed is a valid constant expression so can be used to initialize the constexpr
variable A
. You don't modify the members of the object, so it doesn't matter that they are modifiable.
Clang in particular is very strict about constant expressions, if you were doing something wrong it would give an error.
This object can be used where a constant expression is needed:
constexpr EnumType A5(EnumType::val_A, 5);
e.g.
constexpr int i = A5.getFoo();
This object cannot:
EnumType A6(EnumType::val_A, 6);
constexpr int i = A6.getFoo(); // error
Upvotes: 3