Question

I'm currently implementing some CRTP with a base class template<class CRTP> Base and derived classes Derived1 : public Base<Derived1>, Derived2 : public Base<Derived2>...

The mathematical operators are defined in Base and are of the type CRTP Base<CRTP>::operator+(const CRTP& rhs) that means that we can add a Derived1 to a Derived1 but not a Derived2 to a Derived1.

Furthermore, I have defined the operator Base<CRTP>& Base<CRTP>::operator()() which means that Derived1() will return Base<Derived1>&.

I wonder if there is a solution to do the following :

Derived1 = Derived1 + Derived1 : OK
Derived2 = Derived2 + Derived2 : OK
Derived1 = Derived1 + Derived2 : NOT OK
Derived1 = Derived1() + Derived2() : OK

According to the last two lines :

  • I prevent the user to make a mistake
  • But if he really wants to do this operation he can "cast" the derived types to the base type and it will work

The only thing I need to do this is to define an operator like that :

template<class CRTP0, class = typename std::enable_if</* SOMETHING */>::type> 
Base<CRTP> Base<CRTP>::operator+(const Base<CRTP0>& rhs)

In the enable_if I would like something that is :

  • true : if rhs is of the Base type
  • false : if rhs is a Derived type

Does a such thing exist ? Do you have an other solution in mind ?

Thank you very much !

Was it helpful?

Solution

The /* SOMETHING */ can be easily archived using

  1. std::is_same for the 'false' part of Derived and
  2. A helper class for the 'true' part of Base

The helper class is to determine whether a class is exactly a Base<?>:

template <typename> struct IsBase : std::false_type {};
...
template <typename X> struct IsBase<Base<X>> : std::true_type {};

and then we could fill in that /* SOMETHING */ with:

std::is_same<Other, Self>::value || IsBase<Other>::value

Note that this allows Derived1 + Derived2().


Example: http://ideone.com/OGt0Q

#include <type_traits>

template <typename> struct IsBase : std::false_type {};

template <typename Self>
struct Base {
    Base& operator()() {
        return *this;
    };

    template <typename Other,
              typename = typename std::enable_if<std::is_same<Other, Self>::value
                                              || IsBase<Other>::value>::type>
    Self operator+(const Other& other) const {
        return static_cast<const Self&>(*this);
    }
};

template <typename X> struct IsBase<Base<X>> : std::true_type {};


struct D1 : Base<D1> {};
struct D2 : Base<D2> {};


int main() {
    D1 d1;
    D2 d2;
    d1 + d1; // ok
    d2 + d2; // ok
    d1() + d2(); // ok
    d1 + d2; // error
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top