У меня есть набор операторов, которые мне нужно переопределить для шаблонов выражений. Я хотел бы, чтобы все производные классы базового типа соответствовали базовому типу. Другие вещи будут затем пойманы универсальным типом. К сожалению, универсальный тип захватывает производные типы раньше, чем базовый тип. Чтобы сделать вещи приятными и запутанными, все довольно сильно шаблонизировано, включая некоторые CRTP. Позвольте мне попытаться дать более простую версию кода:
// Note: 'R' is used for return type
template <typename DerivedType, typename R>
class Base
{ // ...
};
template <typename E1, typename E2, typename R>
class MultOperation : public Base<MultOperation<E1, E2, R>, R>
{ // ...
};
template <typename T>
class Terminal : public Base<Terminal<T>, T>
{ // ...
};
// The broken operators:
template <typename T1, typename T2, typename R1, typename R2>
MultOperation<Base<T1, R1>, Base<T2, R2>, typename boost::common_type<R1, R2>::type>
operator*( Base<T1, R1> const& u, Base<T2, R2> const& v)
{
return MultOperation<Base<T1, R1>, Base<T2, R2>, typename boost::common_type<R1, R2>::type>(u, v);
}
template <typename T1, typename T2, typename R1, typename R2>
MultOperation<Terminal<T1>, Base<T2, R2>, typename boost::common_type<T1, R2>::type>
operator*( T1 const& u, Base<T2, R2> const& v)
{
return MultOperation<Terminal<T1>, Base<T2, R2>, typename boost::common_type<T1, R2>::type>(Terminal<T1>(u), v);
}
template <typename T1, typename T2, typename R1, typename R2>
MultOperation<Base<T1, R1>, Terminal<T2>, typename boost::common_type<R1, T2>::type>
operator*( Base<T1, R1> const& u, T2 const& v)
{
return MultOperation<Base<T1, R1>, Terminal<T2>, typename boost::common_type<R1, T2>::type>(u, Terminal<T2>, v);
}
Теперь я не могу использовать какие-либо новые функции C ++. (Это часть некоторых рефакторингов для удаления старых библиотек, чтобы мы могли перейти на новые стандарты cpp.) Однако я могу использовать boost. Я думал, что мой ответ может лежать в boost::enable_if
вещи, но все мои попытки привели к тупику. Теперь имейте в виду, что цель — шаблоны выражений, поэтому я не могу делать какие-либо вещи для приведения данных. Да … это так сложно … Я надеюсь, что у вас есть какая-то магия в вашем рукаве.
Краткая версия вопроса:
Как я могу получить (1 * Derived) * Derived
соответствовать operator(T, Base)
для первого оператора, затем operator(Base, Base)
для второго оператора?
В настоящее время он соответствует первому штрафу, затем второй соответствует одному из базовых универсальных операторов, так как T не принимает преобразования и, следовательно, лучше, чем Base.
Вот черта, которая проверяет, является ли класс Base
:
template<class T>
struct is_some_kind_of_Base {
typedef char yes;
typedef struct { char _[2]; } no;
template<class U, class V>
static yes test(Base<U, V> *);
static no test(...);
static const bool value = (sizeof(test((T*)0)) == sizeof(yes));
};
А затем ограничить ваши последние два operator*
это как:
template <typename T1, typename T2, typename R2>
typename boost::disable_if<is_some_kind_of_Base<T1>,
MultOperation<Terminal<T1>, Base<T2, R2>,
typename boost::common_type<T1, R2>::type> >::type
operator*( T1 const& u, Base<T2, R2> const& v) { /* ... */ }
Предотвращать common_type
чтобы не вызвать серьезную ошибку, нам нужно отложить ее оценку.
template <class T1, class T2, class R1, class R2>
struct make_mult_operation {
typedef MultOperation<T1, T2, typename boost::common_type<R1, R2>::type> type;
};
template <typename T1, typename T2, typename R2>
typename boost::disable_if<is_some_kind_of_Base<T1>,
make_mult_operation<Terminal<T1>, T2, T1, R2> >::type::type
operator*( T1 const& u, Base<T2, R2> const& v) { /* ... */ }
Я понимаю ваш вопрос, что вы хотите специализировать шаблон класса для типов, производных от данного базового типа. Я приведу пример без такого количества параметров шаблона.
Как вы и предполагали, идея в том, чтобы выбрать перегрузки через enable_if
(Я использовал std::enable_if
класс, но вы можете просто заменить std::
от boost::
для ваших целей):
template<typename T, typename U>
struct is_derived_from_base
{
static constexpr bool first = std::is_base_of<Base<T>, T>::value;
static constexpr bool second = std::is_base_of<Base<U>, U>::value;
static constexpr bool none = !first && !second;
static constexpr bool both = first && second;
static constexpr bool only_first = first && !second;
static constexpr bool only_second = !first && second;
};
template<typename T, typename U, typename = typename std::enable_if<is_derived_from_base<T,U>::none>::type >
auto operator*(T const& t, U const& u)
{
std::cout<<"Both T and U are not derived"<<std::endl;
return MultOperation<T, U>(t,u);
}template<typename T, typename U, typename = typename std::enable_if<is_derived_from_base<T,U>::only_first>::type >
auto operator*(Base<T> const& t, U const& u)
{
std::cout<<"T is derived from Base<T>, U is not derived"<<std::endl;
return MultOperation<Base<T>, U>(t,u);
}
template<typename T, typename U, typename = typename std::enable_if<is_derived_from_base<T,U>::only_second>::type >
auto operator*(T const& t, Base<U> const& u)
{
std::cout<<"T is not derived, U is derived from Base<U>"<<std::endl;
return MultOperation<T, Base<U> >(t,u);
}
template<typename T, typename U, typename = typename std::enable_if<is_derived_from_base<T,U>::both>::type >
auto operator*(Base<T> const& t, Base<U> const& u)
{
std::cout<<"T is derived from Base<T>, U is derived from Base<U>"<<std::endl;
return MultOperation<Base<T>, Base<U> >(t,u);
}
Для более подробной информации смотрите полную программу Вот.