Suppose I have a template class which has a template friend function hoping to implement the function of a value multiplying an array(myArray):
template<typename T, int size>
class myArray{
T *_array;
public:
...
template<typename Val, typename Array>
friend myArray<T,size> operator*(const Val &lhs, const Array &rhs){
myArray<T,size> mat_t;
for(int i = 0;i < size; i++)
mat_t._array[i] = lhs * rhs._array[i];
return mat_t;
}
...
};
It works on VS2013. Then I move the definition of the unbound template friend function outside:
template<typename Val, typename Array>
myArray<T,size> operator*(const Val &lhs, const Array &rhs){
myArray<T,size> mat_t;
for(int i = 0;i < size; i++)
mat_t._array[i] = lhs * rhs._array[i];
return mat_t;
}
It is incorrect! I suppose the problem is with the return type of the friend function. But I can't not figure it out. So how to define a friend template function like this outside the class declaration?
friend
functions declared like the first example are strange beasts. Each instance of myArray<T,Size>
creates a distinct friend
function that can only be found via argument dependent lookup on myArray<T,Size>
.
A free operator*
not declared that way does not work that way.
You can get your code to work like this:
template<class Val, class T, int size>
myArray<T,size> operator*(const Val &lhs, const myArray<T,size> &rhs){
myArray<T,size> mat_t;
for(int i = 0;i < size; i++)
mat_t._array[i] = lhs * rhs._array[i];
return mat_t;
}
here all of the template parameters are listed in the template<>
list, and they are all deducible from the arguments to *
.
Remember to put your operator*
in the same namespace as myArray
.
However, personally, I'd go with:
friend myArray operator*(const T&lhs, const myArray &rhs){
myArray mat_t;
for(int i = 0;i < size; i++)
mat_t._array[i] = lhs * rhs._array[i];
return mat_t;
}
a non-template
friend operator*
. Again, one of these is "spawned" for each myArray<T,size>
, but it itself is not a template
. This has certain advantages, such as it behaves nicer with conversion constructors.
Going a step further, we get:
friend myArray& operator*=(myArray&mat, const T&scalar){
for(int i = 0;i < size; i++)
mat._array[i] *= scalar;
return mat;
}
friend myArray operator*(const T&scalar, myArray mat){
mat *= scalar;
return mat;
}
friend myArray operator*(myArray mat, const T&scalar){
mat *= scalar;
return mat;
}
where we first create a *=
, then write *
in terms of it. Notice that *
takes myArray
by-value (because I need to return a copy anyhow, might as well have it happen outside the method), and I support both mat*scalar
and scalar*mat
and mat*=scalar
.
Also note that matrices of matrices ... just work.
The reason why this friend operator
is a good idea is illustrated here. Note the code does not compile. Now #define ADL
to move the operator*
into the class as friends
and it compiles. Koenig operators let operator*
operate on a template
class without being a template, and argument matching rules for templates are hard to get right without being overly narrow, overly broad, or using nasty SFINAE.
Next step, operator*=
can be improved via: (C++14 used for brevity)
template<class Array
class=std::enable_if_t<std::is_same<std::decay_t<Array>,myArray>>
>
friend Array operator*=(Array&&mat, const T&scalar){
for(int i = 0;i < size; i++)
mat._array[i] *= scalar;
return std::forward<Array>(mat);
}
which is more complex, but does fun things with perfect forwarding. A temporary myArray
gets move
d into the return value (which allows for reference lifetime extension to work), while non-temporary myArray
return a reference.
This provides a reason why even *=
should be a friend
. It allows both r and lvalues to be implemented in the same method.
On the other hand, possibly you don't want *=
to work with rvalues. In that case, use a regular method with a &
reference-category qualifier to eliminate that choice.