kimsay
kimsay

Reputation: 321

Overloading operators in C++ with template classes

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.

Upvotes: 2

Views: 903

Answers (3)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275405

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.

Upvotes: 3

George
George

Reputation: 2057

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>.)

Upvotes: 2

songyuanyao
songyuanyao

Reputation: 172924

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;
}

Upvotes: 2

Related Questions