enable_if method specialization

僤鯓⒐⒋嵵緔 提交于 2019-11-29 01:29:54

问题


template<typename T>
struct A
{
    A<T> operator%( const T& x);
};

template<typename T>
A<T> A<T>::operator%( const T& x ) { ... }

How can I use enable_if to make the following specialization happen for any floating point type (is_floating_point)?

template<>
A<float> A<float>::operator%( const float& x ) { ... }

EDIT: Here's an answer I came up which is different from the ones posted below...

template<typename T>
struct A
{
    T x;

    A( const T& _x ) : x(_x) {}

    template<typename Q>
    typename std::enable_if<std::is_same<Q, T>::value && std::is_floating_point<Q>::value, A<T> >::type operator% ( const Q& right ) const
    {
        return A<T>(fmod(x, right));
    }

    template<typename Q>
    typename std::enable_if<std::is_convertible<Q, T>::value && !std::is_floating_point<Q>::value, A<T> >::type operator% ( const Q& right ) const
    {
        return A<T>(x%right);
    }
};

Like the below posters say, using enable_if may not be ideal for this problem (it's very difficult to read)


回答1:


Use overloading instead of explicit specialization when you want to refine the behavior for a more specific parameter type. It's easier to use (less surprises) and more powerful

template<typename T>
struct A
{
    A<T> operator%( const T& x) { 
      return opModIml(x, std::is_floating_point<T>()); 
    }

    A<T> opModImpl(T const& x, std::false_type) { /* ... */ }
    A<T> opModImpl(T const& x, std::true_type) { /* ... */ }
};

An example that uses SFINAE (enable_if) as you seem to be curious

template<typename T>
struct A
{
    A<T> operator%( const T& x) { 
      return opModIml(x); 
    }

    template<typename U, 
             typename = typename 
               std::enable_if<!std::is_floating_point<U>::value>::type>
    A<T> opModImpl(U const& x) { /* ... */ }

    template<typename U, 
             typename = typename 
               std::enable_if<std::is_floating_point<U>::value>::type>
    A<T> opModImpl(U const& x) { /* ... */ }
};

Way more ugly of course. There's no reason to use enable_if here, I think. It's overkill.




回答2:


You can also use a default boolean template parameter like this:

template<typename T>
struct A
{
    T x;

    A( const T& _x ) : x(_x) {}

    template<bool EnableBool = true>
    typename std::enable_if<std::is_floating_point<T>::value && EnableBool, A<T> >::type 
    operator% ( const T& right ) const
    {
        return A<T>(fmod(x, right));
    }

    template<bool EnableBool = true>
    typename std::enable_if<!std::is_floating_point<T>::value && EnableBool, A<T> >::type 
    operator% ( const T& right ) const
    {
        return A<T>(x%right);
    }
};



回答3:


With C++20

You can achieve that simply by adding requires to restrict the relevant template function:

template<typename Q> // the generic case, no restriction
A<T> operator% ( const Q& right ) const {
    return A<T>(std::fmod(x, right));
}

template<typename Q> requires std::is_integral_v<T> && std::is_integral_v<Q>
A<T> operator% ( const Q& right ) const {
    return A<T>(x % right);
}

The requires clause gets a constant expression that evaluates to true or false deciding thus whether to consider this method in the overload resolution, if the requires clause is true the method is preferred over another one that has no requires clause, as it is more specialized.

Code: https://godbolt.org/z/SkuvR9



来源:https://stackoverflow.com/questions/6627651/enable-if-method-specialization

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