2
template<typename T>
class Matrix
{

    template<typename U>
    friend
    Matrix<U> operator+(const Matrix<U>& a, const Matrix<U>& b);


protected:

    size_t _m, _n;
    T *_alloc;
};

template<typename U>
Matrix<U> operator+(const Matrix<U>& a, const Matrix<U>& b)
{
    if(a._m == b._m && a._n == b._n)
    {
        Matrix<U> ret(a._m, a._n);

        // ...

        return ret;
    }
    else
    {
        throw "Matrix dimension mismatch error";
    }
}

I've overloaded operator+ before without problem using non-template classes. Here I'm using a template class.

Matrix<U> Matrix<U>::operator+(const Matrix<U>&, const Matrix<U>&) must take either zero or one argument.

It appears that the compiler is ignoring the friend keyword.

I also tried

friend
template<typename U>
Matrix<U> operator+(const Matrix<U>& a, const Matrix<U>& b);

But this gives me a different compiler error.

expected unqualified-id before 'template'

How can I overload operator+ with template classes?

FreelanceConsultant
  • 13,167
  • 27
  • 115
  • 225

3 Answers3

4

You can overload the + operator using a member function or a non-member function.

When the operator is overloaded using a member function, the LHS of the operator is the object on which the function will be called and RHS of the operator is the argument to the function. Hence, the only argument to the member function will be the RHS.

When the operator is overloaded using a non-member function, the LHS of the operator is the first argument of the to the function and RHS of the operator is the second argument to the function.

Member function

template<typename T>
class Matrix
{
  Matrix operator+(const Matrix& rhs) const {
    ...
  }
};

If you want to implement it outside the class definition, you can use:

template<typename T>
  Matrix<T> Matrix<T>::operator+(const Matrix& rhs) const {
    ...
  }

Non-member function

template<typename T>
class Matrix
{
  Matrix operator+(const Matrix& lhs, const Matrix& rhs) {
    ...
  }
};

If you want to implement it outside the class definition, you need to add some forward declaration code:

// Forward declare the class template
template <typename T> class Matrix;

// Declare the function
template <typename T> Matrix<T> operator+(const Matrix<T>& lhs, const Matrix<T>& rhs);

// Declare the friend in the class definition
template <typename T>
class Matrix
{
   friend Matrix operator+<T>(const Matrix& lhs, const Matrix& rhs);
   //                     ^^^^ 
   // This makes operator+<int> a friend of Matrix<int>, not a friend
   // of Matrix<double>
};

and then implement the function

template <typename T> Matrix<T> operator+(const Matrix<T>& lhs, const Matrix<T>& rhs)
{
    ...
}

With this setup, oprator+<int> is a friend of Matrix<int> only, not a friend of Matrix<double>.

If you use

template <typename U>
friend
Matrix<U> operator+(const Matrix<U>& a, const Matrix<U>& b);

then, all instantiations of operator+ are friends of all instantiations of Matrix, which you don't need.

Update

Sample working code:

#include <iostream>
 
// Forward declare the class template
template<typename T> class Matrix;

// Declare the function
template <typename T> Matrix<T> operator+(const Matrix<T>& lhs, const Matrix<T>& rhs);

// Declare the friend in the class definition
template <typename T>
class Matrix
{
   friend Matrix operator+<T>(const Matrix& lhs, const Matrix& rhs);
   //                     ^^^^ 
   // This makes operator+<int> a friend of Matrix<int>, not a friend
   // of Matrix<double>
};

template <typename T> Matrix<T> operator+(const Matrix<T>& lhs, const Matrix<T>& rhs)
{
   return Matrix<T>{};
}

int main()
{
   Matrix<int> a;
   Matrix<int> b;
   Matrix<int> c = a + b;
}
kinar
  • 402
  • 1
  • 7
  • 22
R Sahu
  • 204,454
  • 14
  • 159
  • 270
2

First, let me advise overloading += instead, then write + in terms of +=. Second, the friend shouldn't be a template typically.

template<typename T>
class Matrix
{
  friend Matrix operator+(Matrix a, const Matrix& b) {
    a += b;
    return std::move(a);
  }
  friend Matrix& operator+=(Matrix& a, const Matrix& b) {
    a.increase_by(b);
    return a;
  }
protected:
  void increase_by( const Matrix& other );

  size_t _m = 0
  size_t _n = 0;
  std::vector<T> _alloc;
};

template<class T>
void Matrix<T>::increase_by(const Matrix<T>& other) {
  if(this->_m == other._m && this->_n == other._n) {
    for (auto i = 0; i < _m*_n; ++i) {
      _alloc[i] += other._alloc[i];
    }
    // ...
  } else {
    throw "Matrix dimension mismatch error";
  }
}

Note that the above, with efficient move, will give you reasonably efficient a+b+c+d+e, as the lhs is created once and repeatedly moved.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 1
    `return std::move(a);` Why the move? The returned value is an rvalue anyways. – Baum mit Augen Mar 07 '16 at 20:33
  • @BaummitAugen RVO cannot occur, the explicit `move` reminds me. – Yakk - Adam Nevraumont Mar 07 '16 at 20:34
  • 1
    Why is that? Why can't `auto c = a + b;` for matrices `a` and `b` not trigger RVO without the `move`? – Baum mit Augen Mar 07 '16 at 20:35
  • 2
    @BaummitAugen You cannot RVO from a function argument. `a` is a function argument of `operator+`. So I cannot RVO from `a` into `operator+`'s return value. I take the function argument `a` by-value to allow outsiders to elide into the object I return, which makes `Matrix x = a+b+c+d+e;` very efficient. I consider the lack of full RVO acceptable, as moving a heap-allocated matrix type is pretty cheap. – Yakk - Adam Nevraumont Mar 07 '16 at 20:49
  • 1
    Thanks, I did not know that. For those who don't know either, [this](https://stackoverflow.com/questions/9444485/why-is-rvo-disallowed-when-returning-a-parameter) is why. – Baum mit Augen Mar 07 '16 at 20:53
0

Solution: Don't explicitly declare templates, they are automatically generated. Perhaps someone can explain why in more detail?

friend
Matrix operator+(const Matrix& a, const Matrix& b)
{
    if(a._m == b._m && a._n == b._n)
    {
        Matrix ret(a._m, a._n);

        return ret;
    }
    else
    {
        throw "Matrix dimension mismatch error";
    }
}
FreelanceConsultant
  • 13,167
  • 27
  • 115
  • 225