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
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 of operator*=
.
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.
Related
I'm learning how to use std::chrono and I want to make a template class Timer easy to use (defined in timer.h). The testing program was successful and everything worked fine, until I tried to use my new Timer in a program with the definition of some template operators, which conflit with the operators used inside Timer.
Inside Timer I have to use operator- between two variables (start_time and end_time) of type std::chrono::time_point, in order to obtain the duration variable containing the elapsed time.
In another header (algebra.h) I implemented the overloading of the binary operator- to make the difference between two std::vector or two std::array, or also a user-defined container provided with operator[] and size() member function.
template<typename pointType>
pointType operator-(pointType a, const pointType & b){
for(int i = 0; i < a.size(); ++i){
a[i] = a[i] - b[i];
}
return a;
}
When I try to include both timer.h and algebra.h, the compiler throws an error saying "ambiguous overload for operator-" suggesting, as possible candidates, both the operator in algebra.h and the one implemented in <chrono>.
I don't understand why it is ambiguous, since pointType can't be deduced as std::chrono::time_point because it doesn't have operator[] and size() member function.
P.S. I tried something else to work it out, but I only got more confused testing a program which use std::valarray. When I include both <valarray> and "algebra.h", and try to make a difference between two valarrays, I expected the compiler to complain about ambiguous definition of operator-, since std::valarray already has implementation for binary operators. But this doesn't happen: it compiles using the <valarray> implementation. Why this doesn't throw an error?
It is ambiguous because the compiler only looks at the function signature to test for ambiguity, not the body of the function. In your example, this is the function signature:
template<typename pointType>
pointType operator-(pointType a, const pointType & b)
Here, the template parameter pointType could be deduced as std::chrono::time_point. However, there is already a binary minus operator declared in the chrono header for std::chrono::time_point (https://en.cppreference.com/w/cpp/chrono/time_point/operator_arith2). This is what is causing the ambiguity error.
To solve this problem, you should first consider whether you need such a generic binary minus operator. The problem you are currently experiencing will not be unique to std::chrono::time_point, but will also occur with any other header that contains a class with a member or non-member binary minus operator, where both arguments are of the same type (or could implicitly convert into the same type). Perhaps a simple set of function overloads for the types in question:
template<typename T>
std::vector<T> operator-(const std::vector<T>& a, const std::vector<T>& b);
template<typename T, size_t N>
std::array<T,N> operator-(const std::array<T,N>& a, const std::array<T,N>& b);
This would be the safest option. You could also not use operator overloading altogether, and stick to a conventional function:
template<typename T>
T pointwise_subtract(const T& a, const T& b);
If you have a c++20 compiler, you could use concepts. If you insist on using non-member operator templates, you may have to use SFINAE-based template metaprogramming, a more advanced and less readable technique:
//enable this template if the type T has a member method "size" and
// subscript operator accepting variables of type "size_t"
template<typename T, typename=std::void_t<
decltype(std::declval<T>().size()),
decltype(std::declval<T>()[std::declval<size_t>()])
>
T operator-(const T& a, const T& b);
This will remove your ambiguity error.
This question already has answers here:
What are the basic rules and idioms for operator overloading?
(8 answers)
Closed 4 years ago.
I have a situation where I have math operations that make sense to overload for convenience.
But some of them operate on other types.
Like
vec3<type> * type
and
type * vec3<type>
One way for the right hand argument for a scalar is:
template<class T, class SubT>
class Vec3 {
// this is fine, ie: works
SubT operator *(const T &val) {
return(SubT(x*val,y*val,z*val));
}
};
Ive read that is is best only to implement operators for *, +, -, /, etc "out of class" or let the compiler deduce things from the += version in the class.
is this optimal vs having + implemented in the class?
how does one do this for the reverse where the left hand argument is the other type?
Ie in my particular case, the templated operator has two template type arguments. One is the type of the element, and the other is the super class the template is implementing it's methods for.
template<class T, class SubT>
SubT operator *(const T &a, const Vec3Base<T, B> &b) {
return(b * a);
}
Anyway hopefully you get my desire, how to do it properly is the question :)
Like do I need to only make to take one type ? ie: the vector type, and then get the element type from it as a typedef ??
template<class VT>
VT::SubT operator*(const VT::ElemT &a, const VT &v) {
return(v * a);
}
and should I also implement the other way instead of in the class but "out of class" with:
template<class VT>
VT::SubT operator*(const VT &a, const VT::ElemT &b ) {
return(VT::SubT(a.x*b,a.y*b,a.z*b));
}
Well I did read most of the answers in the idioims for operator overloading question.
I does answer a lot of things. BUT doesn't cover the ramafacations for templates and templates declaring operators used in the subclasses of those templates.
For all operators where you have to choose to either implement them as a member function or a non-member function, use the following rules of thumb to decide:
This was somewhat helpful in my desire to know the best way to implement whether out of class or in the class.
If it is a unary operator, implement it as a member function.
If a binary operator treats both operands equally (it leaves them unchanged), implement this operator as a non-member function.
If a binary operator does not treat both of its operands equally (usually it will change its left operand), it might be useful to make
it a member function of its left operand’s type, if it has to access
the operand's private parts.
I was wondering if there is an issue regarding prioritization of one over the other for templates. I find if an operator is declared in a template and it is a superclass of a subclass that inherits it's operators at least in the MS compiler it will prioritize looking at the global one over the one in the supreclass. Nasty !!! similar issues happen with clang and gcc.
I did find I really have to declaare all the possible conflicting operators at the same level so the overload resolution works as expected. ie: all in the same superclass of the subclass, if there are poerators declared in a superclass of the superclass, they will sometimes be ignored it seems if there is some wayward conversion that supplies an argument to one of the overloads at the higher priority ( arrrgh ).
It seems at this point I have resolved all the compile issue - now to get it to link hahaha !!
Assuming your type is cheap to move:
template<class T, class SubT>
class Vec3 {
// this is fine, ie: works
SubT& operator *=(const T &val) & {
x*=val;y*=val;z*=val;
return *this;
}
friend SubT operator*(Vec3 v, T const& t){
v*=t;
return v;
}
friend SubT operator*(T const& t, Vec3 v){
v*=t;
return v;
}
};
A 3 tuple of numbers is almost always cheap to move, as the numbers are either tiny (like 64 bits) and trivially copyable, or they are going to be a bignum type which uses a cheap to move storage type internally.
This technique creates what I call Koenig operators; non-template operators onky discoverable via ADL from a template class. This has a myriad of advantages over member function operators and non-member template operators. Less so in this simple case, but as a blind recipie it avoids a bunch of pitfalls (like, how having an operator std::string doesn't let you be << streamed).
I've written a templated class that implements some basic operator overloading, following the guidelines provided by this particularly insightful answer:
template <typename _type>
class myClass {
// ...
template<_input_type> myClass<_type>& operator+=(const myClass<_input_type>& _other);
// ...
}
with the arithmetic compound operators written as members:
template <typename _type>
template <typename _input_type>
myClass<_type>& myClass<_type>::operator+=(const myClass<_input_type>& _other) {
static_assert(std::is_arithmetic<_type>::value);
// do stuff
return *this;
};
and the non-compound operator as a non-member:
template <typename _type, typename _input_type>
inline myClass<_type> operator+(myClass<_type>& _o1, myClass<_input_type> _o2) {
return _o1+=_o2;
};
However, due to the template myClass can be used for several data types, some of them non-numeric that can't handle +,-,* or / operators, and as such I was wondering what are the downsides of implementing all operator overloading code as non-member functions, so that e.g. I could just placed them all on a separate header file that would only need to be included if there is need for arithmetic functionality. I understand one solution would be to define a new class myNumericClass : public myClass that just implements operator overloading, but that would require a new typename and limit the versatility of myClass.
The primary shortcoming of implementing compound assignment as a non-member is inconsistency with the simple (copy or move) assignment operator. A simple copy or move assignment (i.e., operator=) must be implemented as a member function, or the compiler will outright reject the code.
Given that copy/move assignment must be implemented as member functions, many prefer to implement compound assignment as members as well.
As an aside, this code:
template <typename _type, typename _input_type>
inline myClass<_type> operator+(myClass<_type>& _o1, myClass<_input_type> _o2) {
return _o1+=_o2;
};
...is, IMO, highly inadvisable. The general style is fine, but you've mixed up which operand to pass by value and which to pass by reference. As a result, it may be needlessly inefficient, and (much worse) modifies its left operand, so it really acts like += instead of +. What you almost certainly want is more like this:
template <typename _type, typename _input_type>
inline myClass<_type> operator+(myClass<_type> _o1, myClass<_input_type> const &_o2)
{
return _o1+=_o2;
};
Here we pass the left operand by value, so when the function is called a temporary value is created and initialized from the left operand. We then modify that temporary value (without changing the original) and return it. Since we do return it, there will be copy elision (optional on older compilers, but mandatory since C++17) which means it'll normally really just be a reference to the destination, so effectively, something like: a = b + c; will be treated as: a = b; a += c;. Since we only need the previous value of the right operand, we pass it as a reference to const to avoid an unnecessary copy (though, depending on the type, passing by reference may not gain enough to care about, or could even be a loss. But it can be a big gain, and is rarely more than a tiny loss).
I have a matrix class like below:
template <size_t M, size_t N, typename T>
class Matrix
{
public:
Matrix<M, N, T> operator +(const Matrix<M, N, T>& B) const;
template <size_t P> Matrix<M,P,T> operator*(const Matrix<N, P, T>& B) const;
template <typename T2> operator T2() const;
private:
T data[M][N];
};
// ... the body is in header file too ...//
The body has written fine, and everything works well.
When I define two Matrices as below:
Matrix < 10, 10, int> m1;
Matrix < 10, 10, float> m2;
m1 + m2; // OK
m1 * m2; // error: no match for 'operator*' in 'm1 * m2'
The first '+' operator works well, because an implicit casting has performed on it.
but for second '*' operator for different value types, an error occurs.
error: no match for 'operator*' in 'm1 * m2'
Any idea ?!
UPDATE:
All code is in header file. I have no problem but for '*' operator.
What you can say about '+' operator? I know everything about template/operators/casting... but this problem is like a bug for my gcc compiler!? I wrote a cast-operator and this operator calls before '+' operator, but i dont know why it dose not perform for '*' operator!
The problem is more or less classic. The overload resolution starts by
building a list of possible functions; in this case, functions named
operator*. To do this, it adds all operator* functions which are in
scope to the list, and it tries to instantiate all function templates by
applying type deduction; if type deduction succeeds, it adds the
instantiation of the template to the list. (A function template is
not a function. An instantiation of the function template is a
function.)
The rules for template type deduction are different than those used in
overload resolution. In particular, only a very small set of
conversions are considered. User defined conversion operators are not
considered. The result is that in m1 * m2, type deduction for
operator* fails (since it would require a conversion which isn't
considered). So no instantiation of the function template is added to
the list, and there is no other operator*.
More generally: you're operator T2() wouldn't allow type deduction
even if it were allowed; there are a infinite number of conversions
which would match operator*. I suspect, in fact, that you've made it
too general; that you want an operator Matrix<M, N, T2>(). (Not that
this will help here, but there are contexts where it might eliminate an
ambiguity.)
You might be able to make it work by defining a:
template<size_t P, tyepname OtherT>
Matrix<M, P, T> operator*( Matrix<N, P, T> const& rhs ) const;
, then doing the conversion inside the operator*. (I haven't tried it,
and am not sure, but I think your existing operator* should be
considered “more specialized”, and thus be chosen when type
deduction succeeds for both.)
Having said this, I think the way you're doing it is the wrong approach.
Do you really want the return types of m1 * m2 and m2 * m1 to be
different. For starters, I'd require the client code to make the
conversion explicit (which is the case in your current code); if you do
want to support the implicit conversions, I think you need to make the
operator* a global, use some sort of simple meta-programming to
determine the correct return type (i.e. given Matrices of long and
unsigned, you might want to have a return type of unsigned long,
since this is what mixed type arithmetic with these types gives
otherwise), convert both sides to the target type, and do the arithmetic
on it. A lot of work for what is probably not a very important or
useful feature. (Just my opinion, of course. If your clients really
want the mixed type arithmetic, and are willing to pay for it...)
The implicit cast is the culprit in your example (m1 * m1 works). While I am not language-firm enough to tell you exactly why, I suspect that the combination of a templated operator* method (which doesn't specify the type exactly) and a necessary type conversion has too much ambiguity. The compiler is told that it can convert your matrix into any type, and that a templated family of types could be valid arguments for operator*. I would have problems determining which operator* to call from these methods. Inserting a static_cast as m1 * static_cast< Matrix<10,10,int> >(m2) confirms this suspicion.
The Eigen library is a fairly mature and very good matrix library, and they also don't make implicit scalar conversions. Rather, they have used a cast method:
template <typename Scalar> Matrix<M,N,Scalar> cast() const;
In your example, you'd write:
m1.cast<float>() * m2;
I currently have a class hierarchy like
MatrixBase -> DenseMatrix
-> (other types of matrices)
-> MatrixView -> TransposeView
-> DiagonalView
-> (other specialized views of matrices)
MatrixBase is an abstract class which forces implementers to define operator()(int,int) and such things; it represents 2 dimensional arrays of numbers. MatrixView represents a (possibly mutable) way of looking at a matrix, like transposing it or taking a submatrix. The point of MatrixView is to be able to say something like
Scale(Diagonal(A), 2.0)
where Diagonal returns a DiagonalView object which is a kind of lightweight adapter.
Now here's the question(s). I will use a very simple matrix operation as an example. I want to define a function like
template <class T>
void Scale(MatrixBase<T> &A, const T &scale_factor);
which does the obvious thing the name suggests. I want to be able to pass in either an honest-to-goodness non-view matrix, or an instance of a subclass of MatrixView. The prototype as written above does not work for statements such as
Scale(Diagonal(A), 2.0);
because the DiagonalView object returned by Diagonal is a temporary, and Scale takes a non-const reference, which cannot accept a temporary. Is there any way to make this work? I tried to use SFINAE, but I don't understand it all that well, and I'm not sure if that would solve the problem. It is important to me that these templated functions can be called without providing an explicit template argument list (I want implicit instantiation). Ideally the statement above could work as written.
Edit: (followup question)
As sbi responded below about rvalue references and temporaries, Is there a way to define two versions of Scale, one which takes a non-const rvalue reference for non-views, and one which takes a pass-by-value view? The problem is to differentiate between these two at compile time in a way such that implicit instantiation will work.
Update
I've changed the class hierarchy to
ReadableMatrix
WritableMatrix : public ReadableMatrix
WritableMatrixView
DenseMatrix : public WritableMatrix
DiagonalView : public WritableMatrixView
The reason WritableMatrixView is distinct from WritableMatrix is that the view must be passed around by const reference, while the matrices themselves must be passed around by non-const ref, so the accessor member functions have different const-ness. Now functions like Scale can be defined as
template <class T>
void Scale(const WritableMatrixView<T> &A, const T &scale_factor);
template <class T>
void Scale(WritableMatrix<T> &A, const T &scale_factor){
Scale(WritableMatrixViewAdapter<T>(A), scale_factor);
}
Note that there are two versions, one for a const view, and a non-const version for actual matrices. This means for functions like Mult(A, B, C), I will need 8 overloads, but at least it works. What doesn't work, however is using these functions within other functions. You see, each View-like class contains a member View of what it's looking at; for example in the expression Diagonal(SubMatrix(A)), the Diagonal function returns an object of type DiagonalView<SubMatrixView<T> >, which needs to know the fully derived type of A. Now, suppose within Scale I call some other function like it, which takes either a base view or matrix reference. That would fail because the construction of the needed View's require the derived type of the argument of Scale; information it does not have. Still working on find a solution to this.
Update
I have used what is effectively a home-grown version of Boost's enable_if to select between two different versions of a function like Scale. It boils down to labeling all my matrix and view classes with extra typedef tags indicating if they are readable and writable and view or non-view. In the end, I still need 2^N overloads, but now N is only the number of non-const arguments. For the final result, see the here (it's unlikely to get seriously revamped again).
This has nothing to do with templates. Your example
Scale(Diagonal(A), 2.0);
could be generalized to
f(g(v),c);
In C++03, this requires the first parameter to f() to either be passed per copy or per const reference. The reason is that g() returns a temporary, an rvalue. However, rvalues only bind to const references, but not to non-const references. This is independent of whether templates, SFINAE, TMP or whatnot are involved. It's just the way the language (currently) is.
There's also a rationale behind that: If g() returns a temporary, and f() modifies that temporary, then nobody has a chance to "see" the modified temporary. Thus the modification is done in vain and the whole thing is most likely an error.
As far as I understood you, in your case the result of g() is a temporary that's a view onto some other object (v), so modifying it would modify v. But if that's the case, in current C++, the result of g() must either be const (so that it can be bound to a const reference or it must be copied. Since const "smells" wrong to me, making that view cheap to copy would probably be the best thing.
However, there's more to this. C++1x will introduce what's called rvalue references. What we know as "references" will then be divided into either lvalue reference or rvalue references. You will be able to have functions take rvalue references and even overload based on "l/rvalue-ness". This was thought out to allow class designers to overload copy ctor and assignment for rvalue right-hand sides and having them "steal" the right-hand side's values, so that copying rvalues will e cheaper. But you could probably use it to have Scale take an rvalue and modify that.
Unfortunately your compiler very likely doesn't support rvalue references yet.
Edit (followup question):
You cannot overload f(T) with f(T&) to achieve what you want. While only the former will be used for rvalues, lvalues can bind to either argument equally well, so invoking that f with an lvalue is ambiguous and results in a compile-time error.
However, what's wrong with having an overload for DiagonalView:
template <class T>
void Scale(MatrixBase<T> &matrix, const T &scale_factor);
template <class T>
void Scale(DiagonalView<T> view, const T &scale_factor);
Is there anything I'm missing?
Another edit:
I would need a ridiculously large number of overloads then, since there are currently more than 5 views, and there are several dozen functions like Scale.
Then you would need to group together those types that can be handled in the same way. You could use some simple template-meta stuff to do the grouping. Off the top of my head:
template<bool B>
struct boolean { enum { result = B }; };
template< typename T >
class some_matrix {
public:
typedef boolean<false> is_view;
// ...
};
template< typename T >
class some_view {
public:
typedef boolean<true> is_view;
// ...
};
namespace detail {
template< template<typename> class Matrix, typename T >
void Scale(Matrix<T>& matrix, const T& scale_factor, boolean<true>)
{
/* scaling a matrix*/
}
template< template<typename> class Matrix, typename T >
void Scale(View<T>& matrix, const T& scale_factor, boolean<true>)
{
/* scaling a view */
}
}
template< template<typename> class Matrix, typename T >
inline void Scale(Matrix<T>& matrix, const T& scale_factor)
{
detail::Scale( matrix, scale_factor, typename Matrix<T>::is_view() );
}
This particular setup/grouping might not exactly fit your needs, but you can setup something like this in ways that fit for yourself.
An easy way to fix this would be to use boost::shared_ptr< MatrixBase<T> > instead of a reference.
May be, you should use const. ?
template <class T>
void Scale(const MatrixBase<T> &A, const T &scale_factor);
you are limiting the type of the first argument of Scale, but you can let the compiler figure out what type would be appropriate on its own, like this:
template <class M,class T>
void Scale(M A, const T &scale_factor);
Don't use references, pass by value.
Let copy elision do the optimization for you, if needed.