2

I have a template Matrix class and I used a std::vector<std::vector<T>> to store data. I need to specialize some methods for std::complex matrix, for example:

template <typename T>
bool Matrix<T>::is_hermitian() const
{
   if (!(*this).is_squared())
      return false;
   for (int r = 0; r < rows_; r++)
      for (int c = 0; c < columns_; c++)
         if (mat[r][c] != mat[c][r])
            return false;
   return true;
}

For the specialized method I thought something like this:

template <typename T>
bool Matrix<std::complex<T> >::is_hermitian() const
{
   if (!(*this).is_squared())
      return false;
   for (int r = 0; r < rows_; r++)
      for (int c = 0; c < columns_; c++)
         if (mat[r][c] != std::conj(mat[c][r]))
            return false;
   return true;
}

But the compiler returns me an error

'invalid use of incomplete type' 

I instantiated at the end of the .cpp file a bunch of class that I could be using in the main program:

template class Matrix<int>;
template class Matrix<double>;
template class Matrix<float>;
template class Matrix< std::complex<float> >;
template class Matrix< std::complex<int> >;

How can I implement one method for all std::complex<T> type?

And if you know how to replace the last two instance with a Matrix< std::complex<T> > sort of thing I will be very thankful.

JeJo
  • 30,635
  • 6
  • 49
  • 88
  • Did you `#include ` ? – Igor Tandetnik Oct 23 '19 at 23:59
  • Yes, the problem is that the compiler doesn't let me partially specialize a function (or a class) i.e. i can't forward declare a ```template class Matrix>```, i'm looking for a way to turn around the problem – Pierpaolo Vesce Oct 24 '19 at 09:53
  • Possible duplicate of [explicit specialization of template class member function](https://stackoverflow.com/questions/5512910/explicit-specialization-of-template-class-member-function) – quamrana Oct 24 '19 at 13:20

2 Answers2

3

You can apply SFINE(i.e."Substitution Failure Is Not An Error") technique along with function overloading to choose the correct method when T is std::complex in Matrix<T> class instatiation.

Following is the demonstration of the idea: (See example code online live)

#include <type_traits>  // std::enable_if, std::false_type

// traits for checking, T is `std::complex`
template<typename> struct is_std_complex : std::false_type {};
template<typename T> struct is_std_complex<std::complex<T>> : std::true_type {};

// traits for `std::enable_if` helpers
template<typename Type, typename ReType = void>
using EnabledForComplex = typename std::enable_if<is_std_complex<Type>::value, ReType>::type;

template<typename Type, typename ReType = void>
using EnabledNotForComplex = typename std::enable_if<!is_std_complex<Type>::value, ReType>::type;

template<typename T>
class Matrix
{
   // ...members

public:    
   template<typename Type = T>
   auto is_hermitian() const -> EnabledNotForComplex<Type, bool>
   {
      // ... code for non-std::complex types
   }

   template<typename Type = T>
   auto is_hermitian() const->EnabledForComplex<Type, bool>
   {
      // ... code for std::complex types
   }
};

That being said, if you have access to , you could use if constexpr, which will only instantiate the branch, which is true for the case at compile time. (See example code online live)

#include <type_traits> // std::false_type

// traits for checking, T is `std::complex`
template<typename> struct is_std_complex : std::false_type {};
template<typename T> struct is_std_complex<std::complex<T>> : std::true_type {};

template<typename T>
class Matrix
{
   // ...members

public:
   bool is_hermitian() const
   {
      if (!is_squared()) return false;
      if constexpr (is_std_complex<T>::value)
      {
         // ... code for std::complex types
      }
      else
      {
         // ... code for non-std::complex types
      }
      return true;
   }
};
JeJo
  • 30,635
  • 6
  • 49
  • 88
2

You cannot partially specialize function templates, but you can class templates. One trick is to have the member function delegate to a helper class template. Something like this:

template <typename T>
struct IsHermitianHelper;

template <typename T>
class Matrix {
  friend class IsHermitianHelper<T>;
public:
  bool is_hermitian() const;
};

template <typename T>
struct IsHermitianHelper {
  static bool is_hermitian(const Matrix<T>& m) {
    // General implementation here
  }
};

template <typename T>
struct IsHermitianHelper<std::complex<T>> {
  static bool is_hermitian(const Matrix<std::complex<T>>& m) {
    // Special case for std::complex here
  }
};

template <typename T>
bool Matrix<T>::is_hermitian() const {
  return IsHermitianHelper<T>::is_hermitian(*this);
}
Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
  • I arrived at the conclusion that it will cost me more to implement a way around the problem when I can simply copy-paste the functions and change the complex template-argument. I mean, these are useful answers but I was looking for a simplier way that doesn't exist if I got it right. – Pierpaolo Vesce Oct 24 '19 at 14:20