0

I have a matrix class like below:

template <size_t M, size_t N, typename T>
class Matrix
{
public:
    Matrix<M, N, T> operator +(const Matrix<M, N, T>& B) const;
    template <size_t P> Matrix<M,P,T> operator*(const Matrix<N, P, T>& B) const;
    template <typename T2> operator T2() const;  

private:
  T data[M][N];
};

// ... the body is in header file too  ...//

The body has written fine, and everything works well. When I define two Matrices as below:

Matrix < 10, 10, int> m1;
Matrix < 10, 10, float> m2;

m1 + m2;  // OK
m1 * m2;  // error: no match for 'operator*' in 'm1 * m2'

The first '+' operator works well, because an implicit casting has performed on it. but for second '*' operator for different value types, an error occurs.

error: no match for 'operator*' in 'm1 * m2'

Any idea ?!

UPDATE: All code is in header file. I have no problem but for '*' operator.

What you can say about '+' operator? I know everything about template/operators/casting... but this problem is like a bug for my gcc compiler!? I wrote a cast-operator and this operator calls before '+' operator, but i dont know why it dose not perform for '*' operator!

masoud
  • 55,379
  • 16
  • 141
  • 208

2 Answers2

6

The problem is more or less classic. The overload resolution starts by building a list of possible functions; in this case, functions named operator*. To do this, it adds all operator* functions which are in scope to the list, and it tries to instantiate all function templates by applying type deduction; if type deduction succeeds, it adds the instantiation of the template to the list. (A function template is not a function. An instantiation of the function template is a function.)

The rules for template type deduction are different than those used in overload resolution. In particular, only a very small set of conversions are considered. User defined conversion operators are not considered. The result is that in m1 * m2, type deduction for operator* fails (since it would require a conversion which isn't considered). So no instantiation of the function template is added to the list, and there is no other operator*.

More generally: you're operator T2() wouldn't allow type deduction even if it were allowed; there are a infinite number of conversions which would match operator*. I suspect, in fact, that you've made it too general; that you want an operator Matrix<M, N, T2>(). (Not that this will help here, but there are contexts where it might eliminate an ambiguity.)

You might be able to make it work by defining a:

template<size_t P, tyepname OtherT>
Matrix<M, P, T> operator*( Matrix<N, P, T> const& rhs ) const;

, then doing the conversion inside the operator*. (I haven't tried it, and am not sure, but I think your existing operator* should be considered “more specialized”, and thus be chosen when type deduction succeeds for both.)

Having said this, I think the way you're doing it is the wrong approach. Do you really want the return types of m1 * m2 and m2 * m1 to be different. For starters, I'd require the client code to make the conversion explicit (which is the case in your current code); if you do want to support the implicit conversions, I think you need to make the operator* a global, use some sort of simple meta-programming to determine the correct return type (i.e. given Matrices of long and unsigned, you might want to have a return type of unsigned long, since this is what mixed type arithmetic with these types gives otherwise), convert both sides to the target type, and do the arithmetic on it. A lot of work for what is probably not a very important or useful feature. (Just my opinion, of course. If your clients really want the mixed type arithmetic, and are willing to pay for it...)

James Kanze
  • 150,581
  • 18
  • 184
  • 329
2

The implicit cast is the culprit in your example (m1 * m1 works). While I am not language-firm enough to tell you exactly why, I suspect that the combination of a templated operator* method (which doesn't specify the type exactly) and a necessary type conversion has too much ambiguity. The compiler is told that it can convert your matrix into any type, and that a templated family of types could be valid arguments for operator*. I would have problems determining which operator* to call from these methods. Inserting a static_cast as m1 * static_cast< Matrix<10,10,int> >(m2) confirms this suspicion.

The Eigen library is a fairly mature and very good matrix library, and they also don't make implicit scalar conversions. Rather, they have used a cast method:

template <typename Scalar> Matrix<M,N,Scalar> cast() const;

In your example, you'd write:

m1.cast<float>() * m2;  
thiton
  • 35,651
  • 4
  • 70
  • 100
  • Yes, i tried something like `m1.cast()` before, and it was good. but the benefit of implicit-casting is casting like float->float can dropped by compiler. but when i use `cast()` the casting will run even for float->float. – masoud Dec 21 '11 at 09:15
  • @MasoudM: Add a specialization for unnecessary casts. `boost::is_same` is your friend. – thiton Dec 21 '11 at 09:17