user007
user007

Reputation: 2172

Overriding enum values in derived class

I had a class A, which used some enums something like this:

enum DataType
{
    First,
    Second,
    Third
}

And consecutively they had some values assigned to them which was used in code.

Now, I have to write another class B which is very similar to class A, for which I'm planning to make a class Parent and derive both class A and class B from it. But, I want to reuse the enum DataType in both classes.

The problem being, that these enum values should be different for both class A and class B.

For class A

enum DataType
{
    First = 1,
    Second = 2 ...
}

For class B

enum DataType
{
    First = 18,
    Second = 19 ...
}

One naive approach that comes to mind is define a virtual function in both classes, having a switch case, so instead of using the enum, I'll have to call the virtual function and use what it returns.

But, is there a better way to do it? By using some inheritance property, which I'm not aware of?

Upvotes: 1

Views: 1600

Answers (4)

Guillaume Racicot
Guillaume Racicot

Reputation: 41800

I'd simply send the type of the derived class to the parent class function:

// parent not templated!
struct Parent {

    // with this pattern, stuff1 could be virtual:
    // virtual void stuff1() = 0;

protected:
    // static because we can access private
    // member through the self object
    template<typename T>
    static void stuff1_impl(T const& self) {
        auto value = /* get the switched on value */;

        switch(value) {
        case T::DataType::First:
            // things for the case1
            break;
        case T::DataType::Second:
            // things for the case1
            break;
        }
    }
};

Then in your derived classes:

struct A : Parent {
    enum struct DataType {
        First = 1, Second
    };

    //               v---- if virtual, add override
    void stuff1() /* override */ {
        stuff1_impl(*this);
    }
};

struct B : Parent {
    enum struct DataType {
        First = 10, Second
    };

    //               v---- if virtual, add override
    void stuff1() /* override */ {
        stuff1_impl(*this);
    }
};

This pattern avoid templating the whole base class, and you can still use virtual polymorphism. You only templatize the parts that needs the enum in a protected section.

Live example

Upvotes: 1

eerorika
eerorika

Reputation: 238421

A simple solution is to define a enum DataType in each of the members. This introduces no runtime or storage overhead. But in this case the behaviour is static; The user has access to the enum only based on the static type. If the enum values obey a similar pattern to that in your example, you may be able to generate the enum, or even the entire class with a template.

A dynamic approach is what you suggested: Use a virtual function. That has a bit of overhead, but provides runtime polymorphism. In this case, the user can access the enum specific to the dynamic type without knowing what that type is.

These two approaches can even be combined so that you have both.

Upvotes: 2

Maxim Egorushkin
Maxim Egorushkin

Reputation: 136485

One way:

template<int EnumBegin>
struct Parent {
    enum DataType {
        First = EnumBegin,
        Second, // == First + 1
        Third   // == Second + 1
    };
};

struct A : Parent<0> {};
struct B : Parent<10> {};

int main() {
    std::cout << A::Second << '\n'; // Outputs 1.
    std::cout << B::Second << '\n'; // Outputs 11.
}

Upvotes: 1

Lorenz Zhao
Lorenz Zhao

Reputation: 377

Your DataTypes for A and B are distinct types, so the Parent class has to be a template:

template<typename DataType>
class Parent
{
public:
    // example method
    virtual bool isFirst(DataType value) const
    {
        // do something with value, e. g. 
        return (value == DataType::First);
    }
};

enum class DataTypeA
{
    First = 1,
    Second = 2
};
class A : public Parent<DataTypeA>
{
};

enum class DataTypeB
{
    First = 18,
    Second = 19
};
class B : public Parent<DataTypeB>
{
};

int main()
{
    A a;
    B b;

    std::cout << "a.isFirst(DataTypeA::First): " << (a.isFirst(DataTypeA::First) ? "true" : "false") << std::endl;
    std::cout << "a.isFirst(DataTypeA::Second): " << (a.isFirst(DataTypeA::Second) ? "true" : "false") << std::endl;
    std::cout << "b.isFirst(DataTypeB::First): " << (b.isFirst(DataTypeB::First) ? "true" : "false") << std::endl;
    std::cout << "b.isFirst(DataTypeB::Second): " << (b.isFirst(DataTypeB::Second) ? "true" : "false") << std::endl;

    return 0;
}

Note that I used enum class instead of (old-style) enum to prevent running into ambiguity of the enum values.

Upvotes: 1

Related Questions