templates and friends

V

veganders

When making a general matrix class I came across the following
situation that I don't have an explanation for. For some friend
functions I have to declare them in advance while other works anyway.
Could anyone care to explain why I have to do it in this way?

Best regards,
Anders

Code follows below

//operator+ here needs to be declared in advanced or I get a compiler
error while operator* works anyway
template<typename T, int ROWS, int COLS>
class Matrix;

template<typename T, int ROWS, int COLS>
Matrix<T, ROWS, COLS> operator+(
const Matrix<T, ROWS, COLS> & lhs,
const Matrix<T, ROWS, COLS> & rhs);

template<typename T, int ROWS, int COLS>
class Matrix {
public:
Matrix();
// here I have to specify operator+ <T, ROWS, COLS> while
// operator* works fine without
friend Matrix<T, ROWS, COLS> operator+ <T, ROWS, COLS>(
const Matrix<T, ROWS, COLS> & lhs,
const Matrix<T, ROWS, COLS> & rhs);
template<int COLS2>
friend Matrix<T, ROWS, COLS2> operator*(
const Matrix<T, ROWS, COLS> & lhs,
const Matrix<T, COLS, COLS2> & rhs);
template<int ROWS2>
friend Matrix<T, ROWS2, COLS> operator*(
const Matrix<T, ROWS2, ROWS> & lhs,
const Matrix<T, ROWS, COLS> & rhs);
template<int INNER>
friend Matrix<T, ROWS, COLS> operator*(
const Matrix<T, ROWS, INNER> & lhs,
const Matrix<T, INNER, COLS> & rhs);
private:
T mData[COLS][ROWS];
};

template<typename T, int ROWS, int COLS>
Matrix<T, ROWS, COLS>::Matrix() {};

template<typename T, int ROWS, int COLS>
Matrix<T, ROWS, COLS> operator+(
const Matrix<T, ROWS, COLS> & lhs,
const Matrix<T, ROWS, COLS> & rhs) {
Matrix<T, ROWS, COLS> tmp;
for ( int i = 0; i < COLS; i++ ) {
for ( int j = 0; j < ROWS; j++ ) {
tmp.mData[j] = lhs.mData[j] + rhs.mData[j];
}
}
return tmp;
}

// Matrix - Matrix multiplication
template<typename T, int ROWS, int INNER, int COLS>
Matrix<T, ROWS, COLS> operator*(
const Matrix<T, ROWS, INNER> & lhs,
const Matrix<T, INNER, COLS> & rhs) {
Matrix<T, ROWS, COLS> tmp;
for ( int i = 0; i < COLS; i++ ) {
for ( int j = 0; j < ROWS; j++ ) {
tmp.mData[j] = 0;
}
}
for ( int i = 0; i < INNER; i++ ) {
for ( int j = 0; j < COLS; j++ ) {
for ( int k = 0; k < ROWS; k++ ) {
tmp.mData[j][k] += lhs.mData[k] * rhs.mData[j];
}
}
}
return tmp;
}


int main() {
Matrix<float,2,2> A, B;
A+B;
A*B;
return 0;
}
 
V

veganders

When making a general matrix class I came across the following
situation that I don't have an explanation for. For some friend
functions I have to declare them in advance while other works anyway.
Could anyone care to explain why I have to do it in this way?

Best regards,
Anders

Code follows below

//operator+ here needs to be declared in advanced or I get a compiler
error while operator* works anyway
template<typename T, int ROWS, int COLS>
class Matrix;

template<typename T, int ROWS, int COLS>
Matrix<T, ROWS, COLS> operator+(
const Matrix<T, ROWS, COLS> & lhs,
const Matrix<T, ROWS, COLS> & rhs);

template<typename T, int ROWS, int COLS>
class Matrix {
public:
Matrix();
// here I have to specify operator+ <T, ROWS, COLS> while
// operator* works fine without
friend Matrix<T, ROWS, COLS> operator+ <T, ROWS, COLS>(
const Matrix<T, ROWS, COLS> & lhs,
const Matrix<T, ROWS, COLS> & rhs);
template<int COLS2>
friend Matrix<T, ROWS, COLS2> operator*(
const Matrix<T, ROWS, COLS> & lhs,
const Matrix<T, COLS, COLS2> & rhs);
template<int ROWS2>
friend Matrix<T, ROWS2, COLS> operator*(
const Matrix<T, ROWS2, ROWS> & lhs,
const Matrix<T, ROWS, COLS> & rhs);
template<int INNER>
friend Matrix<T, ROWS, COLS> operator*(
const Matrix<T, ROWS, INNER> & lhs,
const Matrix<T, INNER, COLS> & rhs);
private:
T mData[COLS][ROWS];

};

template<typename T, int ROWS, int COLS>
Matrix<T, ROWS, COLS>::Matrix() {};

template<typename T, int ROWS, int COLS>
Matrix<T, ROWS, COLS> operator+(
const Matrix<T, ROWS, COLS> & lhs,
const Matrix<T, ROWS, COLS> & rhs) {
Matrix<T, ROWS, COLS> tmp;
for ( int i = 0; i < COLS; i++ ) {
for ( int j = 0; j < ROWS; j++ ) {
tmp.mData[j] = lhs.mData[j] + rhs.mData[j];
}
}
return tmp;

}

// Matrix - Matrix multiplication
template<typename T, int ROWS, int INNER, int COLS>
Matrix<T, ROWS, COLS> operator*(
const Matrix<T, ROWS, INNER> & lhs,
const Matrix<T, INNER, COLS> & rhs) {
Matrix<T, ROWS, COLS> tmp;
for ( int i = 0; i < COLS; i++ ) {
for ( int j = 0; j < ROWS; j++ ) {
tmp.mData[j] = 0;
}
}
for ( int i = 0; i < INNER; i++ ) {
for ( int j = 0; j < COLS; j++ ) {
for ( int k = 0; k < ROWS; k++ ) {
tmp.mData[j][k] += lhs.mData[k] * rhs.mData[j];
}
}
}
return tmp;

}

int main() {
Matrix<float,2,2> A, B;
A+B;
A*B;
return 0;

}


It should be noted that this is compiled using MSVC. Using g++ I can't
get it to compile at all.
 
G

Gianni Mariani

When making a general matrix class I came across the following
situation that I don't have an explanation for. For some friend
functions I have to declare them in advance while other works anyway.
Could anyone care to explain why I have to do it in this way?

Best regards,
Anders

Code follows below

//operator+ here needs to be declared in advanced or I get a compiler
error while operator* works anyway
template<typename T, int ROWS, int COLS>

You will need one for operator* as well.
class Matrix;

template<typename T, int ROWS, int COLS>
Matrix<T, ROWS, COLS> operator+(
const Matrix<T, ROWS, COLS> & lhs,
const Matrix<T, ROWS, COLS> & rhs);

template<typename T, int ROWS, int COLS>
class Matrix {
public:
Matrix();
// here I have to specify operator+ <T, ROWS, COLS> while
// operator* works fine without
friend Matrix<T, ROWS, COLS> operator+ <T, ROWS, COLS>(
const Matrix<T, ROWS, COLS> & lhs,
const Matrix<T, ROWS, COLS> & rhs);

Below you have three separate operator* declarations. You could have
one only which would fix the ambiguity.
template<int COLS2>
friend Matrix<T, ROWS, COLS2> operator*(
const Matrix<T, ROWS, COLS> & lhs,
const Matrix<T, COLS, COLS2> & rhs);
template<int ROWS2>
friend Matrix<T, ROWS2, COLS> operator*(
const Matrix<T, ROWS2, ROWS> & lhs,
const Matrix<T, ROWS, COLS> & rhs);
template<int INNER>
friend Matrix<T, ROWS, COLS> operator*(
const Matrix<T, ROWS, INNER> & lhs,
const Matrix<T, INNER, COLS> & rhs);
....
 
V

veganders

You will need one for operator* as well.






Below you have three separate operator* declarations. You could have
one only which would fix the ambiguity.


...

I did as you said and it worked but I still can't see how
template<int INNER>
friend Matrix<T, ROWS, COLS> operator*(
const Matrix<T, ROWS, INNER> & lhs,
const Matrix<T, INNER, COLS> & rhs);
can make all 3 needed functions friends which it apparently does. My
guess would have been that this would only permit the function to
access private datamembers in the tmp variable and not in lhs and rhs.
Thanks for the help though, I really appreciate it.
 
B

Barry

When making a general matrix class I came across the following
situation that I don't have an explanation for. For some friend
functions I have to declare them in advance while other works anyway.
Could anyone care to explain why I have to do it in this way?

Best regards,
Anders

Code follows below

//operator+ here needs to be declared in advanced or I get a compiler
error while operator* works anyway
template<typename T, int ROWS, int COLS>
class Matrix;

template<typename T, int ROWS, int COLS>
Matrix<T, ROWS, COLS> operator+(
const Matrix<T, ROWS, COLS> & lhs,
const Matrix<T, ROWS, COLS> & rhs);

actually forward declaration is not needed, some compilers are broken to
well support friend template function in a template class. Of cause MSVC
is included.

But I think your code is at least not right (I don't know is it
ill-formed or not), the three declarations of friend template operator*
are the same.

You only need one of them
 
V

veganders

actually forward declaration is not needed, some compilers are broken to
well support friend template function in a template class. Of cause MSVC
is included.

But I think your code is at least not right (I don't know is it
ill-formed or not), the three declarations of friend template operator*
are the same.

You only need one of them

My thoughts were that that the first friend declaration would capture
the cases when the matrix was the left hand side in the
multiplication, the second when it was the right hand side and the
third when it was the return value. In the full code I also have some
partial specialization for instance 3d-vectors that should also
include functionality for inner products and normalization and cross
product. If I follow the same pattern there I would have
template<int INNER>
friend Matrix<T, 3, 1> operator*(
const Matrix<T, 3, INNER> & lhs,
const Matrix<T, INNER, 1> & rhs);
which I felt wouldn't catch all uses of operator* where a 3d-vector is
involved.
Another question that hasn't been answered yet is why I with the
operator+ must put <T, ROWS, COLS> before ( but with the operator* can
get away without? I havn't tried g++ on this one though as I only got
access to a g++ compiler at school.
 
W

werasm

When making a general matrix class I came across the following
situation that I don't have an explanation for. For some friend
functions I have to declare them in advance while other works anyway.
Could anyone care to explain why I have to do it in this way?

I will attempt to answer your questions.

Firstly, you can declare or define a friend inside a template,
therefore IMHO it should not be necessary to declare things in
advance.

Also (I think this is relevant to your case), if looking at section
14.5.3 (temp.friend), it states that "if the name of the friend is
a qualified-id and a matching specialization ... exists in the
specified
class or namespace (I think this refers to your preceding declaration
of operator+).

The last case states "The name shall be an unqualified id...". For me
this
covers all the cases not mentioned. The reason why your operator*
worked
differently is because is was <unqualified>. As an example, look at
the
following:

template <class T> struct X;

//Required because you qualified operator+
template <class T>
X<T> operator+(
const X<T>& lhs, const X<T>& rhs );

template <class T>
struct X
{
template <class U> friend
bool operator ==( const X<T>& lhs, const X<U>& rhs )
{
return true;
}
//Qualified... requires matching decl...
friend X<T> operator+ <T>(
const X<T>& lhs, const X<T>& rhs );
//Unqualified... this declares a
// non-template!
friend X<T> operator*(
const X<T>& lhs, const X<T>& rhs );

};

int main()
{
X<int> x;
X<double> y;

x+x; //Fails to compile...
x*x; //Compiles OK.

y == x;
x == y;
x == x;
y == y;
return 0;
}

This obviously does not link, but explains what you see.
The fact that you qualified <operator +> requires the
declaration to work. The other thing that you can also
see from the example is that I only require one member
template for operator== to work both ways. For the same
reason you only require one <operator*>. Consider this:
If you instantiated with type T, then the member template
allows comparison with U, where U is any other type including
T. If you instantiate with type U, then the member template
allows comparison with another type (lets call it T), where
T includes any other type including U. I have not searched
the standard for this, but just know its the way it works.

Hope this helps. I attempted to not mislead you.

Regards,

Werner
 
W

werasm

I will attempt to answer your questions.

Firstly, you can declare or define a friend inside a template,
therefore IMHO it should not be necessary to declare things in
advance.

Also (I think this is relevant to your case), if looking at section
14.5.3 (temp.friend), it states that "if the name of the friend is
a qualified-id and a matching specialization ... exists in the
specified
class or namespace (I think this refers to your preceding declaration
of operator+).

The last case states "The name shall be an unqualified id...". For me
this
covers all the cases not mentioned. The reason why your operator*
worked
differently is because is was <unqualified>. As an example, look at
the
following:

template <class T> struct X;

//Required because you qualified operator+
template <class T>
X<T> operator+(
const X<T>& lhs, const X<T>& rhs );

template <class T>
struct X
{
template <class U> friend
bool operator ==( const X<T>& lhs, const X<U>& rhs )
{
return true;
}
//Qualified... requires matching decl...
friend X<T> operator+ <T>(
const X<T>& lhs, const X<T>& rhs );
//Unqualified... this declares a
// non-template!
friend X<T> operator*(
const X<T>& lhs, const X<T>& rhs );

};

int main()
{
X<int> x;
X<double> y;

x+x; //Fails to compile...

Here I meant to say if you don't provide the
declaration in the same namespace. As I did,
it compiled fine.
x*x; //Compiles OK.

Even though no preceding decl exists.

Regards again ;-)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top