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
решая, таким образом, рассматривать ли этот метод в разрешении перегрузки, если предложение требует истинно, то метод предпочтительнее другого, у которого нет предложения требует, поскольку он более специализирован.
Вы также можете использовать логический параметр шаблона по умолчанию, например:
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);
}
};