Reputation: 2655
Let's say I have this class:
in Vector4.h:
#pragma once
template<typename T>
class Vector4
{
public:
T X;
T Y;
T Z;
T W;
Vector4();
Vector4(T X, T Y, T Z, T W);
~Vector4();
friend Vector4<T> operator*(const Vector4<T>& l, const Vector4<T>& r);
};
#include "Vector4.inl"
and in Vector4.inl:
template<typename T>
Vector4<T>::Vector4()
{
X = 0;
Y = 0;
Z = 0;
W = 0;
}
template<typename T>
Vector4<T>::Vector4(T X, T Y, T Z, T W)
{
this->X = X;
this->Y = Y;
this->Z = Z;
this->W = W;
}
template<typename T>
Vector4<T>::~Vector4()
{
}
template<typename T>
Vector4<T> operator*(const Vector4<T>& l, const Vector4<T>& r)
{
return(Vector4<T>(l.X * r.X, l.Y * r.Y, l.Z * r.Z, l.W * r.W));
}
And when I use it somewhere like this:
Vector4<float> a, b;
a = a * b;
it gives me a LNK2019 unresolved external symbol What am I doing wrong?Is the syntax I'm using incorrect?
Upvotes: 1
Views: 1009
Reputation: 39101
As stated in the comments, your friend function declaration
friend Vector4<T> operator*(const Vector4<T>& l, const Vector4<T>& r);
declares a non-template function in the global namespace. When you instantiate e.g. Vector4<int>
, the function
Vector4<int> operator*(const Vector4<int>& l, const Vector4<int>& r)
is declared. Note that it's not a function template. (Also see [temp.friend])
Your Vector4.inl
then declares and defines a function template
template<typename T>
Vector4<T> operator*(const Vector4<T>& l, const Vector4<T>& r)
i.e. an overload of the former function. In the expression a * b
, overload resolution chooses the non-template operator*
over the template version (see [over.match.best]/1). This results in a linker error, as the non-template function hasn't been defined.
As I've almost fooled myself, a short remark:
template<typename T>
Vector4<T> operator*(const Vector4<T>& l, const Vector4<T>& r);
As this operator is a free function (a non-member function), these two lines declare a function template, much like
template<typename T>
Vector4<T> wup();
On the other hand,
template<typename T>
Vector4<T> Vector4<T>::operator*(const Vector4<T>& r)
{ /* ... */ }
defines a member function (non-template) of a class template (Vector4
).
One solution is to use forward-declarations and befriend only a specific specialization:
template<typename T>
class Vector4;
template<typename T>
Vector4<T> operator*(const Vector4<T>& l, const Vector4<T>& r);
template<typename T>
class Vector4
{
public:
T X;
T Y;
T Z;
T W;
Vector4();
Vector4(T X, T Y, T Z, T W);
~Vector4();
// compiler knows of some function template `operator*`,
// can name an specialization:
// ~~~~~~~~~~~~~~~~~~~~~~~~vvv
friend Vector4<T> operator*<T>(const Vector4<T>& l, const Vector4<T>& r);
};
template<typename T>
Vector4<T>::Vector4()
{
X = 0;
Y = 0;
Z = 0;
W = 0;
}
template<typename T>
Vector4<T>::Vector4(T X, T Y, T Z, T W)
{
this->X = X;
this->Y = Y;
this->Z = Z;
this->W = W;
}
template<typename T>
Vector4<T>::~Vector4()
{}
template<typename T>
Vector4<T> operator*(const Vector4<T>& l, const Vector4<T>& r)
{
return(Vector4<T>(l.X * r.X, l.Y * r.Y, l.Z * r.Z, l.W * r.W));
}
int main()
{
Vector4<float> a, b;
a = a * b;
}
Another solution would be to friend the whole operator*
template instead of a single specialization:
template<typename U>
friend Vector4<U> operator*(Vector4<U> const&, Vector4<U> const&);
Upvotes: 4
Reputation: 1433
First of all remove friend
from your declaration of operator*
in the .h file
it should look like this:
Vector4<T> operator*(const Vector4<T>& r);
Then in the .inl file operator* should look like this:
Vector4<T> Vector4<T>::operator*(const Vector4<T>& r){
return(Vector4<T>(X * r.X, Y * r.Y, Z * r.Z, W * r.W));
}
The operand on the left hand side of the operator is what called this member function.
Upvotes: 0
Reputation: 5289
I was able to change the code to nonfriended version:
code inside class definition:
Vector4<T> operator*(Vector4<T> const & r);
code of operator*. It is changed to take only one parameter.
template<typename T>
Vector4<T> Vector4<T>::operator*(Vector4<T> const &r)
{
return(Vector4<T>(this->X * r.X, this->Y * r.Y, this->Z * r.Z, this->W * r.W));
}
Upvotes: 1