mercredi 2 août 2017

How can I define a parent class to reduce code duplication while using an abstract class with an equality operator definition?

I have below as 'simple' of an example of what I am trying to do as I could think up. I have an abstract class A which exposes a public interface to the world with two methods: operator== and performTasksSpecificToA. You can see that I'm using the 'Template Method Pattern' as well as the 'curiously recurring template pattern' in order to ensure that users of A don't need to worry about the implementation of A, in other words AImpl, while still being able to check equality against two instances of AImpl. See this answer on SO for a bit more information and context on this approach.

Now, suppose I wish to define a class B as follows:

    class B
{
    public:
        virtual ~B() = 0;

        bool operator(const B& b) const;
        void performTasksSpecificToB();
};

As you can see, class B shares the same problem as A in terms of defining a public operator== for comparing sub-classes. How can I define a parent-class, let's call it Letter, in order to avoid duplicating code between A and B?


Here is my 'simple example', which compiles and runs.

#include <iostream>

class A
{
    public:
        virtual ~A() = 0;

        bool operator==(const A& a) const;
        void performTasksSpecificToA();
    private:
        virtual bool checkEquality_(const A& a) const = 0;
};

template <class T>
class A_ : public A
{
    protected:
        bool checkEquality_(const A& a) const override;
    private:
        virtual bool checkEquality(const T& t) const = 0;
};

class AImpl : public A_<AImpl>
{
    public:
        AImpl(int val) : val(val){};
        bool checkEquality(const AImpl& anAImpl) const override;
    private:
        int val;
};

A::~A(){}

bool A::operator==(const A& a) const{
    return checkEquality_(a);
}

template <class T>
bool A_<T>::checkEquality_(const A& a) const{
    const T* other = dynamic_cast<const T*>(&a);
    if (other != nullptr){
        const T& me = static_cast<const T&>(*this);
        return other->checkEquality(me);
    }
    return false;
}

bool AImpl::checkEquality(const AImpl& anAImpl) const{
    return val == anAImpl.val;
}

int main(){
    // factory:
    AImpl* aImpl1 = new AImpl(1);
    AImpl* aImpl2 = new AImpl(2);
    AImpl* aImpl3 = new AImpl(1);

    // client:
    A& A1 = *aImpl1;
    A& A2 = *aImpl2;
    A& A3 = *aImpl3;


    std::cout << "A1 == A2 -> ";
    std::cout << (A1 == A2 ? "true" : "false");
    std::cout << std::endl;


    std::cout << "A1 == A3 -> ";
    std::cout << (A1 == A3 ? "true" : "false");
    std::cout << std::endl; 

    delete aImpl1;
    delete aImpl2;
    delete aImpl3;
    return 0;
}

Aucun commentaire:

Enregistrer un commentaire