template method matching derived type instead of base

帅比萌擦擦* 提交于 2019-12-12 01:20:06

问题


I have a set of operators that I need to override for expression templating. I would like all derived classes of a base type match to the base type. Other things would then be caught by a generic type. Unfortunately, the generic type grabs the derived types before the base type does. To make things nice and confusing, everything is templated pretty heavily, including some CRTP. Let me try to give a more simple 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't use any new C++ features. (This is part of some refactors to remove old libraries so we can upgrade to the new cpp standards.) I can use boost stuff, though. I was thinking my answer might lie in boost::enable_if stuff, but all my attempts have led to dead ends. Now, keep in mind that the goal is expression templates, so I can't do any casting stuff for data coming in. Yeah... it's so complicated... I hope you have some magic up your sleeve.

Short version of the question: How can I get (1 * Derived) * Derived to match to operator(T, Base) for the first operator, then operator(Base, Base) for the second operator? It currently matches the first fine, then the second matches to one of the Base-generic operators instead, as T takes no conversion and thereby matches better than Base.


回答1:


Here's a trait that tests whether a class is some kind of 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 constrain your later two operator*s like:

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.




回答2:


I understand your question that you want to specialize a class template for types derived of a given base type. I'll give an example without that many template parameters.

As you suggested, the idea here is to select the overloads via enable_if (I've used the std::enable_if class but you can simply replace std:: by 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);
}

For more details, see the complete program here.



来源:https://stackoverflow.com/questions/30202552/template-method-matching-derived-type-instead-of-base

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!