2

I have the following template class:

    template <class T>
class Matrix {

    public:
        Matrix(size_t rows, size_t columns, const T elements = 0);



        // scalar multiplication
        Matrix<T> operator*(const T& rhs){

            Matrix<T> result(rows, columns);

            for(size_t index = 0; index < rows * columns; ++index){
                result.elements[index] = elements[index] * rhs;
            }

            return result;
        }

        Matrix <T> operator*(const T& lhs, const Matrix<T>& rhs);



        const size_t rows;
        const size_t columns;


        private:

            std::vector<T> elements;
};

and the following implementation of the operator* :

// scalar multiplication
template <class T>
Matrix<T> Matrix<T>::operator*(const T& lhs, const Matrix<T>& rhs){

    Matrix<T> result(rhs.rows, rhs.columns);

    for(size_t index = 0; index < rhs.rows * rhs.columns; ++index){
        result.elements[index] = elements[index] * lhs;
    }
    return result;
}

when I try to compile clang says: error: overloaded 'operator*' must be a unary or binary operator (has 3 parameters)|

And I don't quite understand, what I am missing in this. In general template classes give me a hard time when it comes to overloading operators and I don't know why. There have been some posts on here on SO on this topic and I tried a few variations of the code but none of them worked.

kimsay
  • 311
  • 3
  • 15
  • as a member function it has `this` implicitly as first parameter but you want to add 2 more. Lets say you have `Matrix a,b,c;` your operator would be called like `a.operator*(b,c)` does this make sense? no ;) – 463035818_is_not_an_ai Mar 11 '16 at 15:43
  • Related: http://stackoverflow.com/questions/35853204/c-overloading-operator-in-a-template-class – R Sahu Mar 11 '16 at 15:47

3 Answers3

3

The simple and reasonably efficient way to solve this problem is as follows:

  1. Implement Matrix& operator *=(SomeType const&) and similar operations first. These are mutating operations that change an instance of the class, then return a reference to *this.

  2. Implement other operations as inline friends in terms of *=, where the lhs (usually) argument is taken by-value, modified and returned.

This tends to be really simple and quite often more efficient than starting with operator* instead of operator*=.

So you'll have:

 template<class T, etc>
 struct Matrix{
   Matrix& operator*=(T const&);
   Matrix& operator*=(Matrix const&);
   Matrix& operator+=(Matrix const&);

which you implement traditionally. Then:

   friend Matrix operator*(T const& t, Matrix m){ m*=t; return m; }
   friend Matrix operator*(Matrix m, T const& t){ m*=t; return m; }

   friend Matrix operator*(Matrix lhs, Matrix const& rhs){ lhs*=rhs; return lhs; }
   friend Matrix operator+(Matrix lhs, Matrix const& rhs){ lhs+=rhs; return lhs; }

and now you only need to implement a few, traditional, methods.

These friend operators are non-template inline non-method functions that are auto-generated for each template instance of Matrix.

Your immediate problem was your non-static operator actually took an implicit this in addition to its two explicit parameters, and binary operators cannot take 3 arguments.


The complex and even more efficient solution involves a technique often called "expression templates". The disadvantage is that expression templates are more complex to write, and have a few points of fragility around the auto keyword and similar situations.

As an example:

Matrix m = m1 * m2 + m3 * m4 + m5 + m6;

An expression template will do the above with only one allocation of the internal data of a matrix.

My above code will copy m1, multiply the result by m2. Then it will copy m3, then multiply that by m4. Then it will add up everything without making any additional copies. Finally, this result will be moved into m.

So two matrices will be created instead of 1 in the expression template case.

A more-naive solution (like the OP's design) would create 5 Matrices instead of 2.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • thank you! Very often I'm not sure if I should implement a function within the class definition or outside of it. Is there a general rule? – kimsay Mar 11 '16 at 16:22
  • 1
    @kimsay If it is short and you are ok with `inline` and its perils, do it in the class definition. If it isn't, put it elsewhere? In the case of the above Koenig friend operators, you *must* do it in the class definition (as there is no way to refer to the operator outside of the class): this also means (to me) that they should always be short. – Yakk - Adam Nevraumont Mar 11 '16 at 16:27
  • also the friend is only there to access private fields? – kimsay Mar 11 '16 at 16:34
  • 1
    @kimsay nope: it does a bunch of things. It makes ADL work smoothly, makes the operators themselves non-template (which has some nice properties: template operators can be fragile), it makes them non-member binary operators, etc. – Yakk - Adam Nevraumont Mar 11 '16 at 16:37
  • sorry, got it! Thank you! @ Yakk – kimsay Mar 11 '16 at 16:44
2

You're declaring Matrix <T> operator*(const T& lhs, const Matrix<T>& rhs); as member function, which has an implicit parameter this, that why compiler complains it "has 3 parameters".

You can make it a free template function,

template <class T>
class Matrix {
    ...
    template <class Z>
    friend Matrix<Z> operator*(const Z& lhs, const Matrix<Z>& rhs);
    ...
};

and

// scalar multiplication
template <class Z>
Matrix<Z> operator*(const Z& lhs, const Matrix<Z>& rhs){

    Matrix<Z> result(rhs.rows, rhs.columns);

    for(size_t index = 0; index < rhs.rows * rhs.columns; ++index){
        result.elements[index] = elements[index] * lhs;
    }
    return result;
}
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
2

Your function is a member function. Member functions have a hidden parameter, the this pointer.

You either need to make your operator* a non-member function or you need to get rid of one of the arguments to your operator* function (which will then multiply the data in "this" with the data in the incoming Matrix<T>.)

George
  • 2,034
  • 1
  • 15
  • 16