Template problems...

Started by
6 comments, last by eyal 18 years ago
Hey! I have a class CNumber which has complex number functionality, it is a template class, so I can do something like this:

CNumber<float>    fNumber;    // number with single precision
CNumber<double>   dblNumber;  // double prec.
CNumber<int>      nNumber;    // contains only integers

This has not been a problem, the problem arises in my also templated CMatrix class, which encodes matrix functionality. Particularly one of the member funtions has made me confused, namely the function IsHermitian(). This function returns true if the matrix is hermitian (is equal to the transpose of it's conjugated). To check this I have to conjugate every single element in the matrix, but this is only possible if the type of the matrix data elements is CNumber!:

template <class T> class CMatrix
{
private:
     T*    m_Matrix;
     int   m_dimX, m_dimY;

public:
     BOOL  IsHermitian();
     ...
};

For example, if I do something like this:

CMatrix<double>    dblMatrix;   // matrix with all data elements of type double
CMatrix<CNumber<double >> numMatrix; // matrix with all data members CNumber<double>

dblMatrix.IsHermitian();   // won't work because double has ofcourse no member func. Conjugate()
numMatrix.IsHermitian();   // this is fine, CNumber has a member func. Conjugate();

CNumber has a member function which returns the conjugate of itself but double does not. But IsHermitian() need to conjugate the values of the matrix! Why am I facing this problem? I suppose I have misunderstood something about templates? Thanks in advance!
Advertisement
So what is the problem exactly? I'm not sure what the standard says, but my understanding is that the code for a templated function is only created for the types for which it's invoked. If you only ever call IsHermitian() on a CMatrix<CNumber>, you shouldn't get any compilation errors.

If that doesn't answer your question, perhaps you could clarify. (Also, you might consider dropping the 'C' prefix from your class names.)
Thanks for the quick answer!

Well, the problem is the cases in which IsHermitian() is called on for example a CMatrix<double> object. This will generate compiler errors, but the function call should be valid. CMatrix<double> is obviosly a real matrix (cannot contain complex values) so IsHermitian() should return true if the matrix is symmetric, even on CMatrix<double> objects. And if I have to assume that IsHermitian() is only called on CMatrix<CNumber> objects, what is the point of making it a template anyway? The template should be generic right?
Quote:Original post by Repetit
Thanks for the quick answer!

Well, the problem is the cases in which IsHermitian() is called on for example a CMatrix<double> object. This will generate compiler errors, but the function call should be valid. CMatrix<double> is obviosly a real matrix (cannot contain complex values) so IsHermitian() should return true if the matrix is symmetric, even on CMatrix<double> objects. And if I have to assume that IsHermitian() is only called on CMatrix<CNumber> objects, what is the point of making it a template anyway? The template should be generic right?
Oh, I see - you want to be able to call IsHermitian() on any matrix type, but the behavior should be different depending on whether the elements are real only, or whether some of them can be complex or imaginary.

There are various ways to accomplish this; I won't presume to suggest a 'best' way, but I'll tell you one way it can be done. You could make IsHermitian() a non-member function, and specialize it:
template < class T >bool IsHermitian(const Matrix<T>& m){    // Matrix is real - just check if it's symmetric}bool IsHermitian(const Matrix<Number>& m){    // Matrix may have complex elements - perform full check}
I realize you currently have it as a member function, and may want to keep it that way. In this case there are other techniques you could employ, such as defining a numerical_traits<> class for the element type that determines the behavior of the function. If you're writing a matrix class that is intended to be used with complex and imaginary numbers as well as real, you'll probably encounter this problem again, so it might be a good idea to get the architecture in place for it now.

[Edit: Or use eyal's solution, below...]
You can implement a specialization of the IsHermitian function for the int/double/float/... types, which will only check whether the matrix is symmetric (if the values in the matrix are real, then a symmetric matrix is hermitian).
template<>BOOL CMatrix<int>::IsHermitian() { /* check symmetry */ }

Thanks again both of you! I think I will be going for the specialization method and implement functions like:

BOOL CMatrix<int>::IsHermitian() { /* check symmetry */ }


This seems like a good solution. But doesn't it kinda ruin the purpose of templates, generalization? And a final question, could you briefly explain what you mean by a numerical_traits<> class? What would the purpose of it be? :-)
Quote:Original post by Repetit
This seems like a good solution. But doesn't it kinda ruin the purpose of templates, generalization?
Not really. Templates are a tool, and tools are supposed to be useful. Specialization makes templates a more flexible and useful tool, so in that sense it is in keeping with their purpose, rather than counter to it.
Quote:And a final question, could you briefly explain what you mean by a numerical_traits<> class? What would the purpose of it be? :-)
Here is a nice summary of various template programming techniques, including the 'traits class'.
Quote:Original post by Repetit
But doesn't it kinda ruin the purpose of templates, generalization?

Well, when you use templates, you're supposed to write code tht will work for all types of data you allow to use as template parameters. You wrote a function which uses a conjugate operator which isn't available for some types (C++'s POD types).

Anther approach is to define a conjugate operator for the data types you wish to use: write an external conjugate function which accepts a CNumber and returns its conjugate, and overload it to accept any POD types and have it return the value it received (since they're all real). Something like:
template<typename T>CNumber<T> conjugate(const CNumber<T>& val){   return val.conj();}int conjugate (int val) {return val;}float conjugate (float val) {return val;}

You could then use this function in your IsHermitian() function. This way your IsHermitian() function works "as planned" - with all the types for which a conjugate operator is defined.

This topic is closed to new replies.

Advertisement