Question

I'm trying to build a class such that its subclasses will have a C++11 enum class attribute, with an associated setter/getter:

class Base { 
  public:
    enum class MyEnum {
        A,
        B
    } my_enum;

    Base() : my_enum(MyEnum::A) {}

    MyEnum get() { return my_enum; }
    void set(const MyEnum &ME) { my_enum = ME; } 
};

class Derived : public Base { 
  public:
    Derived(): Base() {
        my_enum = MyEnum::B;
    }
};

int main(int argc, char *argv[])
{
    Derived Deriv;

    // No problems!
    Deriv.get() == Derived::MyEnum::B;  

    return 0;
}

so far, so good!

However, I would like derived classes to be able to re-define the MyEnum enumeration class, while not having to re-implement the setter/getter/attribute all the time:

// Base as before

class Derived : public Base {
  public:

    enum class MyEnum {
        C,
        D 
    }; // intention: to override Base's "my_enum" attribute

    Derived(): Base() {
        my_enum = MyEnum::C;  
        // ERROR: cannot convert 'Derived::MyEnum' to 'Base::MyEnum' 
    }
};

int main(int argc, char *argv[])
{
    Derived Deriv;

    // ERROR: no match for 'operator==' for types 'Base::MyEnum' and 'Derived::MyEnum'
    Deriv.get() == Derived::MyEnum::C;  

    return 0;
}

I understand what the problem is; I'm just looking for the cleanest way to be able to reuse code for this case.

Preferably only through inheritance (or rather, the functionality should be available to Derived() classes solely by the act of deriving from Base()).

Any suggestions?

Was it helpful?

Solution

You could make Base a template, parameterised by the enum type, and use a traits class to provide a "default" enum type which can be specialized by derived types.

template<typename T>
struct MyEnumTraits
{
    enum class type {
        A,
        B
    };

    static const type default_value = type::A;
};

template<typename T = void>
class Base { 
  public:

    typedef typename MyEnumTraits<T>::type MyEnum;
    MyEnum my_enum;

    Base() : my_enum(MyEnumTraits<T>::default_value) {}

    MyEnum get() { return my_enum; }
    void set(const MyEnum &ME) { my_enum = ME; } 
};

class Derived;

template<>
struct MyEnumTraits<Derived>
{
    enum class type {
        C,
        D 
    };
    static const type default_value = type::C;
};

class Derived : public Base<Derived> {
    // ...

But now different derived types will have different base classes, which is probably not what you want. You could solve that by keeping the non-template Base and moving the getter and setter into an intermediate class template that derivecs from Base and then the derived types derive from that.

class Base { ... };

template<typename T = void> class GetterSetterImpl : public Base { ... };

class Derived : public GetterSetterImpl<Derived> { ... };

OTHER TIPS

The compiler is right: although the enumerations Base::MyEnum and Derived::MyEnum are defined in classes connected through inheritance, the enumerations themselves are not considered related. They just happen to have the same unqualified name, which means nothing to the compiler: as far as the compiler is concerned, the two enum types are unrelated.

If you think about the way the enums are implemented under the hood, this makes sense: despite being strongly typed, the enums remain small integral constants. Since the two are unrelated, Base::MyEnum::A would have the same value as Derived::MyEnum::C, and there would be nothing at runtime letting you distinguish between the two values.

Besides dumping all enumeration values into the enum of the base class (which kills opportunities to extend outside your own library) there is little you can do: C++ enumerations do not support inheritance, and are generally not very flexible.

This is a bad idea and is not going to work.

  • Language level You cannot redefine stuff in C++. You can only hide (not completely) stuff behind new, unrelated stuff with the same name. You can also override a virtual function by giving it a new implementation, while keeping its signature.
  • Design level Your base class defines a contract which derived classes must adhere to. If Base::get() returns a MyEnum {A, B}, then every derived class must have a get() that returns a MyEnum {A, B}. That's what inheritance is all about (and not code reuse, or not just code reuse at any rate).

You may be able to achieve code reuse by making your class into a template instead of relying on inheritance.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top