How to change a template method based on whether the type is an integral or floating point type?

StackOverflow https://stackoverflow.com/questions/10283610

Question

I'm working on a Matrix class which takes both integral (short, int, long) and floating point types (float, double). I want some methods to be restricted to only floating point types (such as the inversion method) and some methods to have different implementations for floating types and integral types (such as the == operator). I have a hunch the right way is to use boost's "enable_if" and "is_integral" / "is_floating_point", but I can't seem to get it to work.

My implementation is something similar to this c++ semi-pseudo code:

template <typename T>
class Matrix
{
    ...
    bool operator==(Matrix<typename enable_if<is_integral<T> T >::type >) const;
    bool operator==(Matrix<typename enable_if<is_floating_point<T>::type T> >) const;
    typename enable_if<is_floating_point<T> T> computeInverse() const;
    ...
};
// implementation
bool Matrix<T>::operator==(Matrix<typename enable_if<is_integral<T> T >::type >) const {
  //implementation without precision
}
bool Matrix<T>::operator==(Matrix<typename enable_if<is_integral<T> T >::type >) const {
  //implementation using precision
}
Matrix<typename enable_if<is_floating_point<T> T>::type > Matrix<T>::computeInverse() const {
  //implementation requiring floating points
}

This produces a lot of compile errors, whereas I think these are the most relevant ones:

error: no type named ‘type’ in ‘struct boost::enable_if<boost::is_integral<float>, float>’

and

error: no type named ‘type’ in ‘struct boost::enable_if<boost::is_floating_point<int>, int>’

This indicates that I can't have different implementations for different types, at least not using boost's enable_if, is this correct?

If so, how do I do this? I know that template specialization would be a way to go, but I would like to avoid duplicating too much code.

Was it helpful?

Solution

The simplest would be to use an overloaded function in Matrix:

template <typename T>
class Matrix
{
    template <bool isInteger> class Discrim;
    //  ...
    bool isEqual( Matrix const& other, Discrim<true> ) const
    {
        //  Integer implementation...
    }

    bool isEqual( Matrix const& other, Discrim<false> ) const
    {
        //  Floating point implementation...
    }

public:
    bool isEqual( Matrix const& other ) const
    {
        return isEqual( other, Discrim<std::numeric_limits<T>::is_integer>() );
    }
};

Your operator== and operator!= will call Matrix::isEqual, of course.

Having said that: judging from your comments, you want an "almost equal" function if T is a floating point typ. Don't do this. It will only confuse people, and cause no end of problems down the road (since == will no longer be a transitive operation).

OTHER TIPS

You should try and follow @DavidRodriguez's advice and separate the common functionality of your class into a base class; then provide specializations of the whole derived class where functionality differs. That would be the better approach.

If you do want to keep your current implementation, you can SFINAE the undesired version of the operator out of overload resolution as follows:

#include <iostream>
#include <type_traits>

template<class T>
struct Matrix
{
  // ...
};

template<class T>
typename std::enable_if<
    std::is_integral<T>::value, bool
  >::type
operator==( const Matrix<T>&, const Matrix<T>& )
{
  std::cout << "Integer version" << std::endl;
  return true;
}

template<class T>
typename std::enable_if<
    !std::is_integral<T>::value, bool
  >::type
operator==( const Matrix<T>&, const Matrix<T>& )
{
  std::cout << "Floating point version" << std::endl;
  return true;
}

int main()
{
  Matrix<int> m1, m2;
  Matrix<double> m3, m4;

  if( m1 == m2 ) {}

  if( m3 == m4 ) {}
}

If you want the operators as member functions the only way I know of to make it work is to add a dummy template parameter to the operators and add && std::is_same<T,U> to the std::enable_if test condition.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top