Arithmetic compound operator overload as non-member - c++

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).

Related

Enabling implicit conversion from `vector<const T>&` to `const vector<T>&`

Suppose we define a class template<typename T> class vector that behaves like std::vector except that we are able to alter its class definition.
Suppose also that we have a function f(const vector<T>&).
How can we enable an implicit conversion that would allow us to pass a vector<const T>& into f?
I think such an implicit conversion is sensible, because I believe the restrictions imposed by the const vector<T>& are a superset of the restrictions imposed by the vector<const T>&. But any guidance that might be enlightening would be appreciated.
Supposing that f is indeed a function for some concrete T, as opposed to a function template (i.e. template<typename T> f(vector<T> const&)), then the following operator inside of vector<T> should work:
template<typename U = T, typename = std::enable_if_t<std::is_const<U>{}>>
operator vector<std::remove_const_t<U>>() const {
// ...
}
Online Demo
However, f cannot be a standalone function template as its T will simply be deduced to be itself const and no conversion will take place. Overloading can solve this, but will be ugly.
N.b. this is presumably just making a copy of the elements inside of the original vector; returning a vector<T> const& that somehow aliases the internals of the original vector<T const> is a significantly different task, as it would involve extra lifetime-management state and/or questionably-legal type aliasing.

Compiler throws "ambiguous overload for operator"

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.

How does one declare an out of class binary operator for a templated class? [duplicate]

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).

Understanding how declval is working in the copy_assignment situation

I was watching Dr. Walter E. Brown's Template Meta-programming talk. In his presentation he presents code like so for is_copy_assignable:
template<class U, class = decltype(declval<U&>() = declval<U const&>())>
static true_type try_assignment(U &&);
What I am having trouble with is how is the assignment operator being called in this instance. When I try and reason about this code and say substitute a dummy type, say int, in place of the U I get:
template<class U, class = decltype(declval<int&>() = declval<int const&>())>
template<class U, class = decltype(int& = int const&)>
So I am wondering then how can this help us determine if the assignment operator is valid. If I understand declval correctly this code will not even evaluate so then how can one determine from int& = int const&, which doesn't even evaluate, if there is or isn't a assignment operator defined for U.
I understand that in most cases the copy-assignment operator would be defined as
C& operator=(const C& other)
which looks a lot like what is above, but still since nothing is being evaluated then what use is this information.
I don't really follow what you intended in performing the following step:
template<class U, class = decltype(declval<int&>() = declval<int const&>())>
template<class U, class = decltype(int& = int const&)>
declval says: pretend to return a value of type of the template argument. I say pretend because the function isn't really defined, only declared, so it can only be used in an unevaluated context. Inside of decltype is an unevaluated contexts because you're only checking types.
So, the expression decltype(declval<int&>() = declval<int const&>()) is basically saying: if I had an int&, call it x, and I had a int const&, call it y, what is the type of the expression x = y?
As you can probably guess this calls the assignment operator and this expression will have a valid type (in this case, int&). If you change int to unique_ptr, this expression won't have a valid type, because unique_ptr is not assignable, causing type substitution to fail and eliminating this template from the overload or specialization set.
The reason to have declval at all is because it lets you create a value of any type; this is particularly useful for a) creating something of reference type, and b) creating non-reference types without assuming that they have e.g. a default constructor. Thus, use of declval is extremely ubiquitous in high quality TMP.

No match operator for 'operator *' in Matrix

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;