c++ operator-overloading for a template struct - c++

I have a template struct "point as follows:
template<typename T>
struct point
{
T x, y, z;
template<typename T1>
inline point<T> operator*(const point<T1>& p) const // multiply by another point.
{
return point<T>{this->x*p.x, this->y*p.y, this->z*p.z};
}
template<typename T1>
inline point<T> operator*(const T1& v) const // multiply by constant from right side
{
return point<T>{this->x*v, this->y*v, this->z*v};
}
}
template<typename T1, typename T2>
inline point<T1> operator*(const T2& v, const point<T1>& p) // multiply by a constant from the left side.
{
return point<T1>{p.x*v, p.y*v, p.z*v};
}
The two operator overloading functions that declared as member functions, the first one is supposed to multiply by another point, and the other to multiply a point by a constant from the right side, where the one declared outside the struct is to do the same thing but from the left side.
And now when I go to compile the following code:
point<double> p1{1,2,3};
point<float> p2{1,2,3};
point<double> p3 = p1*p3;
The compiler calls the one declared outside the struct instead of the one declared as a member function, and produces and error:
error: cannot convert ‘point<double>’ to ‘float’ in initialization
return point<T1>{p.x*v, p.y*v, p.z*v};
And that makes sense, because the both arguments are template variables and can be interpreted as point variables. Now the second one can be only a point variable, but the first one can be anything!
To solve this I can go and write several copies of that function, when declaring the first argument once as int, float, double, long double .. et cetera. This works fine for me, but I'm still wondering if there is a better way to deal with this, and if I can write only one copy as above?

Do not unnecessarily overload operators as member functions,
follow the rules described here.
Having operator* for two point instances return the type of the left one does not make sense, in my opinion. Better consider both types in the deduction of the return type, since the function is defined as commutative.
template<typename T>
struct point
{
T x, y;
};
template<typename T1, typename T2>
auto operator*(const point<T1>& lhs, const point<T2>& rhs)
-> point<decltype(std::declval<T1>() * std::declval<T2>())>
{
return {lhs.x*rhs.x, lhs.y*rhs.y};
}
template<typename T1, typename T2>
point<T1> operator*(const point<T1>& p, const T2& v)
{
return {p.x*v, p.y*v};
}
template<typename T1, typename T2>
point<T1> operator*(const T2& v, const point<T1>& p)
{
return p * v;
}
Try to do the same with the overloads for multiplication by a scalar as an exercise.

Related

operator< redefinition on a std::pair<T1,T2> in class template

I've got the following situation in a class template case:
template<class T1,class T2>
class targetClass{
public:
typedef typename std::pair<T1, T2> ToSortType;
typedef typename std::set<ToSortType> ContainerSort;
void bar(ToSortType a, ToSortType b);
private:
ContainerSort container;
bool operator<(const ToSortType& rhs) const;
}
template<class T1,class T2>
void targetClass<T1,T2>::bar(ToSortType a, ToSortType b){
container.insert(a);
container.insert(b);
}
template <class T1,class T2>
bool targetClass<T1,T2>::operator<(const ToSortType& rhs) const
{
return this->first < rhs.first;
}
In main function something like this:
targetClass<int,T2> anObjectTarget;
T2 a;
T2 b;
anObjectTarget.bar(std::make_pair(0,a),std::make_pair(1,b));
Where T2 is a user-defined type which generally does not have a defined operator<
In this particular case, std::set has to compare std::pair<int,T2> by firstly check an operator< (and others, maybe) for int type and then for T2. In this case, the compiler can not find a suitable operator for T2. In the previous snippet then I make a redefinition of the operator concerned but the compiler complains in this way:
/usr/include/c++/7/bits/stl_pair.h:456: error: no match for ‘operator<’ (operand types are ‘const T2’ and ‘const T2’)
|| (!(__y.first < __x.first) && __x.second < __y.second); }
~~~~~~~~~~~^~~~~~~~~~~~
I've never redefined an operator before but looking to the documentation it looks correct to me (but not to the compiler).
The operator you overloaded is a member of targetClass<T1,T2> and takes two ToSortType as parameter. Thats not how an overload of the < operator works. Consider that for instances of class type the follwing two are equivalent:
a < b
a.operator<(b)
ie operators are just syntactic sugar for calling special member functions. The operator you wrote could only be called like
targetClass<T1,T2> t;
T1 a;
T2 b;
t.operator<(a,b);
but what the set tries to call is a < b, ie a.operator(b) and that apparently does not exist (std::pair<T1,T2> can only be comared via < when both T1 and T2 can).
Long story short: You cannot use your operator to compare two instances of ToSortType.
I would not recommend to try to overload the operator< for std::pair<T1,T2>, but rather use a custom type:
template<class T1,class T2>
class targetClass{
public:
struct value_type {
T1 first;
T2 second;
bool operator<(const value_type& other) {
return first < rhs.first;
}
}
using container_type = std::set<value_type>;
void bar(const value_type& a,const value_type& b);
private:
container_type container;
};
If you want to stay with std::pair then you can use the fact that std::set allows you to chose the type of the comparator. However, first I have to explain a bit not to confuse you because the following may appear to be contradicting the above (it does not). The default comparator that set uses is std::less<Key>, that is a type with an operator() that compares two element of type Key, it is something similar (but not exactly) like this:
template <typename Key>
struct less {
bool operator() (const Key& a,const Key& b) const {
return a < b;
}
};
And this is the place where the compiler cannot find a < for your Key type (which is std::pair<T1,T2>). You can use your own comparator:
template <typename T1,typename T2>
struct my_comparator {
bool operator() (const std::pair<T1,T2>& a, const std::pair<T1,T2>& b) const {
return a.first < b.first;
}
};
And then your set is
using container_type = std::set<typename std::pair<T1,T2>,typename my_comparator<T1,T2>>;

How to promote two template types for arithmitic operations like builtin types do?

If I have a generic struct/class:
template<typename T>
struct Container
{
T value;
Container(const Value& value) : value(value) { }
};
And I want to perform an operation on two of them:
template<typename T, typename U>
Container<T> operator+(const Container<T>& lhs, const Container<U>& rhs)
{
return Container<T>(lhs.value + rhs.value);
}
The problem is that if lhs is of the type Container<int> and rhs is of the type Container<float>, then I'll get a Container<int> back. But if I were to do auto result = 2 + 2.0f, then result would of of type float. So the behavior is inconsistent between builtin types and custom types.
So how would I take the operator+ overload and make it return Container<float>, much like how C++ handles arithmetic promotion with builtin types?
As far as you use one of the two types of the template, you risk to induce a cast on the result of the sum. As an example, if you accidentally choose int as your target type, even though the sum results in a float, it will be cast down to the chosen type.
Anyway, starting with C++11, you con rely on the decltype specifier as in the example above (or at least, you can do that if Container::T and Container::U are a types for which the + operator is defined).
I used also the auto specifier as return value for the operator+, for it is at our disposal starting from C++14 and it's really useful indeed.
It follows the working example above mentioned:
#include <iostream>
#include <vector>
template<typename T>
struct Container
{
T value;
Container(const T& value) : value(value) { }
};
template<typename T, typename U>
auto operator+(const Container<T>& lhs, const Container<U>& rhs)
{
return Container<decltype(lhs.value+rhs.value)>{lhs.value + rhs.value};
}
int main()
{
Container<int> c1{1};
Container<float> c2{0.5};
std::cout << (c1+c2).value << std::endl;
}

Express preference in case of ambiguous templated functions

Given the following code:
struct Zero{};
template<typename T>
Zero operator*(const Zero& zero, const T& other){return Zero();}
struct Identity{};
template<typename T>
T operator*(const T& other, const Identity& id){return T();}
Now, I want to use this code like this:
Zero z;
Identity id;
int i = 5;
z * i; // ok
i * id; // ok
z * id; //error: ambiguity in function resolution
The compiler will not be able to resolve the operator in the last line, because both functions could be used. In fact in this case I do not care which function is used, since they have the same functionality. In both cases Zero() will be returned as expected.
My Question: How can I express that in this case any of the functions are ok to use?
Just add one more overload (which is not a template whatsoever):
Zero operator*(const Zero& other, const Identity& id){return Zero();}
Just use SFINAE to remove the first template from consideration if T is Identity:
template<typename T> auto operator*(const Zero& zero, const T& other)
-> typename std::enable_if<!std::is_same<T, Identity>::value, Zero>::value
{return {};}

Templates and casting

I created a matrix class with templates:
template <typename T>
class Matrix
{
static_assert(std::is_arithmetic<T>::value,"");
public:
Matrix(size_t n_rows, size_t n_cols);
Matrix(size_t n_rows, size_t n_cols, const T& value);
// Functions
// Operators
Matrix<T>& operator*=(const T& value)
private:
size_t rows;
size_t cols;
std::vector<T> data;
};
I created the following two (external) operators to multiply my matrix with a number:
// Inner operator used by the externals ones
template <typename T>
inline Matrix<T>& Matrix<T>::operator*=(const T& value)
{
for(size_t i(0); i < data.size(); i++)
{
data[i] *= value;
}
return *this;
}
template <typename T>
inline Matrix<T> operator*(const T& value, const Matrix<T>& matrix)
{
Matrix<T> tmp(matrix);
return tmp *= value;
}
template <typename T>
inline Matrix<T> operator*(const Matrix<T>& matrix, const T& value)
{
return value * matrix;
}
The problem is that if I declared the matrix as a double, I can multiply the matrix only by doubles and so on...
Matrix<double> m1(3,3,1.);
5. * m1; // Works
5 * m1; // Doesn't work (5 is an int and not a double)
How can I fix this behave? It is possible to let doubles be multiplied by others arithmetic types?
Sure, just allow two parameters to your templated free functions and member functions.
For example:
template <typename T> class Matrix {
/* ... */
template <typename U>
inline Matrix<T>& operator*=(const U& value)
{
for(size_t i(0); i < data.size(); i++)
{
data[i] *= value;
}
return *this;
}
};
template <typename T, typename U>
inline Matrix<T> operator*(const U& value, const Matrix<T>& matrix)
{
Matrix<T> tmp(matrix);
return tmp *= value;
}
This will trigger compiletime errors if you try to multiply your Matrix with something nonsensical, that is, if T*U is undefined.
Yes. Declare the function in the Matrix class as
template <typename T>
class Matrix
{
public:
/* ... */
template <typename S>
inline Matrix & operator*=( const S & value );
/* ... */
};
The definition looks like
template <typename T>
template <typename S>
inline Matrix<T>& Matrix<T>::operator*=(const S& value)
{
for(size_t i(0); i < data.size(); i++)
{
data[i] *= value;
}
return *this;
}
for the member function. You need to write template twice. A bit odd, but that's C++ syntax.
In case of the free functions you can write
template <typename T, typename S>
inline Matrix<T> operator*(const S& value, const Matrix<T> &mat)
{
Matrix<T> tmp(mat);
return tmp *= value;
}
The problem that you are seeing is that template type deduction requires a perfect match of all of the deduced types. In your case you have a template that takes a single type argument T that is both the scalar and the matrix types. When the compilers sees the operation: 5 * m1, it deduces T == int for the first argument but T == double for the second argument, and type deduction fails.
There are multiple approaches around this, as it has been suggested, you can add a second template argument:
template <typename T, typename S>
Matrix<T> operator*( Matrix<T> m, S scalar ) {
return m*=scalar;
}
[Note: both arguments by value, the second one because for arithmetic types it is more efficient and idiomatic to pass by value; the first one because by moving the copy to the interface of the function you allow the compiler to elide copies]. This approach is simple, but will generate one operator* for each combination of S and T in the program, even though the actual multiplication in operator*= is always performed on T.
Another approach would be to fix the type of the scalar that you want to multiply by, for example, make it double, generating only one operator* per T type that is multiplied:
template <typename T>
Matrix<T> operator*( Matrix<T> m, double scalar ) {
return m*=scalar;
}
In this approach there is a single operator*, the one taking a double as argument. As in the previous example, it might require two type conversions on the scalar (say you multiply Matrix<int> by 5, it will then convert 5 into a double, which will then be converted back to int to match the signature of operator*=.
The third approach is to create a non-templated function that takes your Matrix and another argument of the same type. This will be the closest to your original code, with the slight advantage that not being a template, it will allow conversions for the scalar argument. Theoretically you could define all such functions yourself manually:
Matrix<int> operator*( Matrix<int>, int ) { ... }
Matrix<double> operator*( Matrix<double>, double ) { ... }
But this becomes a maintenance problem very easily. Luckily, there is a feature in the language that allows for the definition of all those non-template functions generically. Although the syntax might not be the most natural. You just need to declare the free function as a friend of your template, and define it inside the class template definition:
template <typename T>
class Matrix {
// ...
friend Matrix operator*( Matrix m, T scalar ) { return m*=scalar; }
};
As we are inside the class template Matrix, we can use Matrix (without arguments) to refer to the current instantiation (Matrix<int>, Matrix<double...) [This might not seem obviously important, but it is, it is important to realize when Matrix refers to the template, and when it refers to the class generated from the template]. The second argument to the function is T. Again, this is not the generic T of the class template, but the current instantiating type (int, double...).
The language allows for the definition of a friend function inside the class that has the declaration, and that will define the function at namespace level, although the declaration will only be found through Argument Dependent Lookup.
Whenever you instantiate a particular instance of your template (say Matrix<int>) and call the operator, the compiler will generate the free function for you. Because the function is not templated, it will allow conversions on the arguments, and thus for Matrix<int> m it will allow you to call m * 5. by converting 5. into an int.

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 __x[M][N];
};
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 addition works well, because an implicit casting has performed on it, but for the multiplication of different value types, an error occurs.
error: no match for 'operator*' in 'm1 * m2'
Any idea ?!
This question has a similar problem. The reason for you error is that implicit conversions are not considered when deducing template arguments. Since your multiplication operator is a function-template, and you call without explicitly providing the parameter, the compiler tries to deduce the argument type and fails. To demonstrate, if you explicitly provide the parameter P, it compiles:
m1.operator*<10>(m2);
To fix the problem, you could make the value-type of the right-hand-side a template-argument too:
template <size_t P, typenmame T2>
Matrix<M,P,T> operator*(const Matrix<N, P, T2>& B) const;