enable_if разрешает только базовый класс

В настоящее время я реализую некоторый CRTP с базовым классом template<class CRTP> Base и производными классами Derived1 : public Base<Derived1>, Derived2 : public Base<Derived2>...

Математические операторы определены в Base и имеют тип CRTP Base<CRTP>::operator+(const CRTP& rhs), что означает, что мы можем добавить Derived1 к Derived1, но не Derived2 к Derived1.

Кроме того, я определил оператор Base<CRTP>& Base<CRTP>::operator()(), что означает, что Derived1() вернет Base<Derived1>&.

Интересно, есть ли решение сделать следующее:

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

Судя по последним двум строчкам:

  • Я предотвращаю ошибку пользователя
  • Но если он действительно хочет выполнить эту операцию, он может «привести» производные типы к базовому типу, и это сработает.

Единственное, что мне нужно сделать, это определить такой оператор:

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

В enable_if я хотел бы что-то:

  • true : если правая сторона имеет тип Base
  • false : если rhs имеет тип Derived

Существует ли такая вещь? У вас есть другое решение?

Большое спасибо !


person Vincent    schedule 18.08.2012    source источник


Ответы (1)


/* SOMETHING */ можно легко заархивировать с помощью

  1. std::is_same для «ложной» части Derived и
  2. Вспомогательный класс для «истинной» части Base

Вспомогательный класс должен определить, является ли класс именно Base<?>:

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

а затем мы могли бы заполнить это /* SOMETHING */ следующим образом:

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

Обратите внимание, что это позволяет Derived1 + Derived2().


Пример: 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
}
person kennytm    schedule 18.08.2012