14
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)

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
David
  • 27,652
  • 18
  • 89
  • 138

3 Answers3

33

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.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • I appreciate the answer and I do like your solution, but I don't agree with why you think template specialization is wrong. If it is possible to specialize for multiple types using enable_if I would still like to know how – David Jul 08 '11 at 17:36
  • @Dave because you can use overloading instead. Where you have a parameter and you want to specialize for parameter types, overloading is better. No you cannot "specialize with `enable_if`". What you mean is overloading. I'm going to add an example that overloads and uses SFINAE. – Johannes Schaub - litb Jul 08 '11 at 17:37
  • This may sound stupid but I don't like opModImpl being there, even if it's private. Also I don't like the idea of potentially having an extra temporary created there, even though the optimizer should get rid of it. C++0x should have just included a way to have multiple specializations use the same definition in the first place – David Jul 08 '11 at 17:41
  • 1
    @Dave : Every library I'm aware of that makes significant use of TMP uses this exact technique over SFINAE solutions whenever possible. I.e., your misgivings are unwarranted. ;-] – ildjarn Jul 08 '11 at 17:42
  • 5
    @Johannes Your enable_if solution doesn't compile for me... `error C4519: default template arguments are only allowed on a class template` `error C2535: 'A A::opModImpl(const U &)' : member function already defined or declared` – David Jul 08 '11 at 17:55
  • @Dave since you marked your question `[C++0x]` I was using a C++0x feature (default template arguments for function templates). – Johannes Schaub - litb Jul 08 '11 at 17:57
  • @Johannes Oh, ok - yeah I'm really just using the subset in VS2010. Is it possible to change what you've done into a specialization of operator% instead of using opModIml? And is it possible without default function template arguments? – David Jul 08 '11 at 18:01
  • @Dave : "*Is it possible to change what you've done into a specialization of operator% instead of using opModIml?*" `operator%` is not a template -- how can one specialize a non-template? – ildjarn Jul 08 '11 at 21:17
  • @ildjarn See exactly what I posted at the top: template<> A A::operator%( const float& x ) { ... }See edit in the original post for a different solution I came up with – David Jul 08 '11 at 21:26
  • @Dave : In what you posted at the top (pre-edit), `A<>` is a template, `A<>::operator%` is not. This is why you had to make your `operator%` overloads themselves templates in your edit's code. – ildjarn Jul 08 '11 at 21:51
  • What exactly would you call a specialization of a method of a templated class? It's not a class specialization. My semantics still seem correct. Oh, and in my edit there is no specialization going on whatsoever so I'm going to disregard your second sentence – David Jul 08 '11 at 22:30
  • @Dave : I'll clarify -- if we're being pedantic, your pre-edit code has an explicit specialization of a non-template member function of a class template. However `enable_if` can only operate in the contexts of partial specializations or overloads, so the fact that your pre-edit code contains an explicit specialization doesn't prove anything relevant to what I was trying to convey. (cont'd) – ildjarn Jul 08 '11 at 23:21
  • @Dave : (cont'd) It is not possible to partially specialize functions -- your post-edit code contains overloads, not specializations, but it would have been impossible to create overloads for SFINAE purposes _without_ making them templates. This is all I was trying to point out. – ildjarn Jul 08 '11 at 23:22
  • @ildjarn we were on the same page basically from the start. I didn't understand enable_if when I first posted the question – David Jul 08 '11 at 23:30
5

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);
    }
};
Paul Fultz II
  • 17,682
  • 13
  • 62
  • 59
5

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

Amir Kirsh
  • 12,564
  • 41
  • 74