enable_if специализация метода

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

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

Как я могу использовать enable_if, чтобы сделать следующую специализацию для любого типа с плавающей точкой (is_floating_point)?

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

РЕДАКТИРОВАТЬ: Вот ответ, который я пришел, который отличается от тех, которые размещены ниже...

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

Как говорят приведенные ниже плакаты, использование enable_if может быть не идеальным решением для этой проблемы (ее очень трудно прочитать)

2 ответа

Решение

Используйте перегрузку вместо явной специализации, когда вы хотите уточнить поведение для более определенного типа параметра. Проще в использовании (меньше сюрпризов) и мощнее

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

Пример, который использует SFINAE (enable_if) как вам кажется любопытным

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

Гораздо страшнее, конечно. Там нет причин для использования enable_if здесь, я думаю. Это излишне.

С C++20

Вы можете добиться этого, просто добавив requires чтобы ограничить соответствующую функцию шаблона:

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

В requires статья получает constant expression что оценивается как true или false решая, таким образом, рассматривать ли этот метод в разрешении перегрузки, если предложение требует истинно, то метод предпочтительнее другого, у которого нет предложения требует, поскольку он более специализирован.

Код: https://godbolt.org/z/SkuvR9

Вы также можете использовать логический параметр шаблона по умолчанию, например:

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);
    }
};
Другие вопросы по тегам