问题
I'm writing a template matrix class, and I am a bit confused as to how overloading the * operator would work.
I would want to overload something like this (omitting irrelevant code):
template<typename T>class Matrix4t
{
friend vector3 operator*(const vector3 &inputV3, const matrix4t &inputM4t);
template <typename scalarT>
friend scalarT operator*(const scalarT &inputSclT, const matrix4t &inputM4t);
public:
const matrix4t operator*(const matrix4t)
vector3 operator*(const vector3 &inputV3);
template <typename scalarT>
const matrix4t operator*(const scalarT&);
}
Assuming correct definitions for the individual multiplications I assume this should allow multiplication of my matrix object with an object of type vector3 from both sides of the operand, returning a vector3 each time. Also it should allow multiplication of my matrix with a scalar template value, which could be float, double etc. The definitions of these methods would need to be different, to allow for vector vs scalar multiplication, hence not just using the template for both
On using this operator with a vector3 though, would the compiler know to use the explicitly declared vector3 method, or would it try and create the templated version of the method, which would not work as the definition is written to only allow for a scalar value, and probably also then complain about method redefinition.
Any thoughts on if this would work, or how else I could go about it?
Cheers
回答1:
I have the feeling that you might be aiming for a more complex solution than needed, so I will start building from the ground up, and leave some details for later. First I will start analyzing your proposed syntax and what it means.
friend vector3 operator*(const vector3 &v, const matrix4t &m);
template <typename scalarT>
friend scalarT operator*(const scalarT &inputSclT, const matrix4t &inputM4t);
These are friend declarations. Friend declarations declare (tell the compiler that there is) an entity external to the class and that such an entity should be granted full access to the internals of this class (which in this case is a template). The second friend declaration is of a free function template operator*
that takes a scalarT
type and a matrix4T<T>
object both by const reference and yields an scalarT
value. This friend declaration seems strange in that the multiplication of a matrix by a scalar value usually yields another matrix of the same dimensions, rather than just a scalar value.
Note that inside the class template, the name of the template matrix4t
does not refer to the template, but the particular specialization (i.e. it represents matrix4t<T>
, not the name of the template). The distinction might not seem important now, but sooner or later you will realize the importance of it.
The second declaration is of a non-templated free function operator*
that takes a vector3
and a matrix4t
both by const reference and yields another vector3
. Since we are inside the definition of the matrix4t
, the name of the template refers to the specialization matrix4t<T>
, but vector3
refers just to the template, and not any particular instantiation. You should either make that a template that accepts vector3<U>
for any given type U
, or else a non-templated function that accepts a single type (which can be the T
argument of our template): vector3<T>
. In the same way as with the other declaration, the return value might be off or not... depends on what are the dimensions of the vector (is it row or column?)
Regarding the actual befriending, I recommend that you read this answer to a different similar question.
As of the actual operators, for the scalar type, I would suggest the following approach:
- Implement
operator*=
taking the scalar of the same type as is stored as an argument - Implement both versions of
operator*
in terms ofoperator*=
.
template <typename T> class matrix4t {
public:
matrix4t& operator*=( T scalar ); // and implement
friend matrix4t operator*( matrix4t lhs, T scalar ) {
return lhs*=scalar;
}
friend matrix4t operator*( T scalar, matrix4t rhs ) {
return rhs*=scalar;
}
};
Notes: operator*=
is implemented once, taking the matrix as left hand side (you could also offer an overload taking the matrix as right hand side, but it would look a bit strange: 5 *= matrix
yielding a matrix...). Implement operator*
as free functions (friendship is only used to provide the free function for each instantiating type, read the linked answer), by forwarding to operator*=
.
For the case of multiplying by a vector (and because it is not symmetric) the trick of dispatching to a single implementation will not work, but you can provide the two implementations as non-templated friends as above.
If you wanted to provide operations with mixed types, then all of the above would have to be templates. The added complexity is not the template, but rather determining what the result types should be if you want to promote the types. With C++11 the easiest way would be using decltype
and trailing return types:
template <typename U>
friend auto operator*( matrixt4 lhs, U scalar ) -> matrix4t< decltype(scalar * element(0,0) ) > {
// implement here
}
And similarly for operator*(scalar,matrix)
. Note that if you are promoting types, operator*=
might not make sense at all (as the type would be the same as the lhs), or if it did, it might or might not produce the result that you want. If you consider doing it, the operator+
would have to take the matrix4t<A>
argument by reference (as it might not be of the appropriate type) and copy into the matrix4t<B>
return type (where A
and B
are respectively the types of the matrix being multiplied and the result).
From here on, you should decide what you need or want to implement, and you might want to ask more specific questions as they come by.
来源:https://stackoverflow.com/questions/11854371/c-template-operator-overload