Template method matching derived type instead of base

I have a set of statements that I need to override for an expression template. I would like all derived classes of the base type to match the base type. Other things will then be caught by the generic type. Unfortunately, the generic type captures derived types to the base type. To make things nice and confusing, all the templates are pretty strong, including CRTP. Let me try to give a simpler version of the code:

// 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); } 

Now I can not use any new C ++ features. (This is part of some refactors for removing old libraries, so we can upgrade to the new cpp standards.) I can use boost. I thought that my answer might lie in boost::enable_if , but all my attempts led to deadlocks. Now keep in mind that the target is expression templates, so I cannot do any input material. Yes, it is so complicated ... I hope you have the magic up your sleeve.

Short version of the question: How can I get (1 * Derived) * Derived to match operator(T, Base) for the first operator, and then operator(Base, Base) for the second operator? Currently, it corresponds to the first penalty, then the second corresponds to one of the basic operators instead, since T does not accept the conversion and is thus better than Base.

0
source share
2 answers

Here is a trait that checks if a class is somehow 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)); }; 

And then limit your subsequent two operator* as follows:

 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) { /* ... */ } 

Demo

To prevent common_type from causing a hard error, we need to defer its evaluation.

 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) { /* ... */ } 

Demo

+1
source

I understand your question, that you want to specialize a class template for types derived from a specific base type. I will give an example without these template parameters.

As you suggested, the idea is to select overloads via enable_if (I used the std::enable_if class, but you can just replace std:: with boost:: for your purposes):

 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); } 

See the full program here for more details.

0
source

Source: https://habr.com/ru/post/987223/


All Articles