Reputation: 627
In my code, I implement a template of Vector3 like below:
template <typename T>
struct TVector3{
TVector3(const T& x, const T& y, const T& z); // normal constructor from x,y,z
TVector3(const T& val); // construct from a constant value
// .. other implementation
T x, y, z;
};
// and my overload operator+ for TVector3
template <typename T>
const TVector3<T> operator+(const TVector3<T>& lhs, const TVector3<T>& rhs)
{
return TVector3<T>(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z);
}
I'm expecting call like this: (1, 2, 3) + 1 = (2, 3, 4), so I wrote it like this:
TVector3<float> v(1, 2, 3);
v + 1.0f; // error, on operator "+" matches ... operand types are: TVector3<float> + float
Then I seem to understand that template deduction should completely match argument types instead of converting them, so is there any way for me to tell compiler to convert T to TVector3? I don't want to write 2 overloads like below because code would become large:
template <typename T>
const TVector3<T> operator+(const TVector3<T>& lhs, const T& rhs);
template <typename T>
const TVector3<T> operator+(const T& rhs, const TVector3<T>& rhs);
Thanks for helping!
Upvotes: 1
Views: 67
Reputation: 2149
The trick here is to instantiate a function that is not actually a member when the class template gets instantiated. This can be accomplished via a friend definition (inline friend). When the class template is instantiated, the friend is also instantiated, and also injected into the surrounding namespace (in a restricted way) as a non-templated function, so that conversions will be applied. (I also made the return non-const, since I don't think you intended that.) For further details, lookup "friend name injection", "argument dependent lookup", and the related Barton-Nackman trick.
template <typename T>
struct TVector3{
friend TVector3<T> operator+(const TVector3<T>& lhs, const TVector3<T>& rhs) {
return TVector3<T>(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z);
}
TVector3(const T& x, const T& y, const T& z); // normal constructor from x,y,z
TVector3(const T& val); // construct from a constant value
// .. other implementation
T x, y, z;
};
int
main() {
TVector3<float> v(1, 2, 3);
v + 1.0f; // Works.
}
An important and related thing to note is that there is a difference between a template function and a non-template function, even if the signature is otherwise the same.
template <typename T> class A;
void f(A<int>); // 1
template <typename T>
void f(A<T>); // 2
template <typename T>
class A {
friend void f(A<T>); // Makes 1 a friend if T == int, but not 2.
friend void f<>(A<T>); /// Makes 2 also a friend.
};
Upvotes: 5