Commutative operator overloading + of 2 different objects - c++

I have 2 classes which represent a matrix:
1. RegularMatrix - O(n^2) representation
2. SparseMatrix - a matrix that is represented as linked list (without zeros).
lets say i have:
RegularMatrix a;
SparseMatrix b;
i want to be able to do:
a+b;
and also:
b+a;
so i'm overloading the + operator. My question is, since I want the addition to be commutative (a+b = b+a), do i need to implement 2 overloadings, one for each case?
RegularMatrix operator+(const RegualarMatrix &, const SparseMatrix &);
RegularMatrix operator+(const SparseMatrix & ,const RegualarMatrix &);
or is there a general form which the compiler decides by itself?
Thank you

Yes you need both versions. But you can forward the one to the other, if the operation really is commutative
RegularMatrix operator+(const SparseMatrix &a, const RegualarMatrix &b) {
return b + a;
}

Both versions are required, just write after first overload:
RegularMatrix operator+(const SparseMatrix &a, const RegualarMatrix &b)
{
return operator+(b,a);
}
or simpler version:
RegularMatrix operator+(const SparseMatrix &a, const RegualarMatrix &b)
{
return b + a;
}

Unless you have a huge amount of operators with complicated signatures that all need to be duplicated for commutative behaviour, I would just use the solution from the accepted answer. However, if you really hate repeating your code or want to make it work just for the sake of it:
#include <iostream> // std::cout
#include <utility> // std::pair
#include <type_traits> // std::remove_cvref_t
#include <concepts> // std::same_as
// These two utilities will be used for all commutative functions:
template <typename T, typename U, typename V, typename W>
concept commutative =
(std::same_as<std::remove_cvref_t<T>, V> && std::same_as<std::remove_cvref_t<U>, W>) ||
(std::same_as<std::remove_cvref_t<U>, V> && std::same_as<std::remove_cvref_t<T>, W>);
template <typename V, typename W, typename T, typename U>
requires commutative<T, U, V, W>
constexpr decltype(auto) order (T && a, U && b) {
if constexpr (std::same_as<std::remove_cvref_t<T>, V>)
return std::pair{std::forward<T>(a), std::forward<U>(b)};
else
return std::pair{std::forward<U>(b), std::forward<T>(a)};
}
// Here goes the use-case:
struct A {
int aval;
};
struct B {
int bval;
};
// This template declaration allows two instantiations:
// (A const &, B const &) and (B const &, A const &)
template <typename T, commutative<T, A, B> U>
A operator + (T const & first, U const & second) {
// But now we need to find out which is which:
auto const & [a, b] = order<A, B>(first, second);
return {.aval = a.aval + b.bval};
}
// Just to test it:
int main () {
A a = {.aval = 1};
B b = {.bval = 2};
A c = a + b;
A d = b + a;
std::cout << c.aval << '\n';
std::cout << d.aval << '\n';
}
If template <typename T, commutative<T, A, B> U> and auto const & [a, b] = order<A, B>(first, second); is less boilerplate code than repeating the whole definition with return b + a; for your scenario, then I guess it could be useful.

Related

Template constraints on operator overloading does not work as expected

#include <tuple>
#include <utility>
template<typename T>
struct is_tuple_like : std::false_type {};
template<typename... Ts>
struct is_tuple_like<std::tuple<Ts...>> : std::true_type {};
template<typename T, typename U>
struct is_tuple_like<std::pair<T, U>> : std::true_type {};
template<typename T>
concept tuple_like = is_tuple_like<T>::value;
template<tuple_like L, tuple_like R, int N = std::tuple_size_v<L>>
auto operator*(const L &lhs, const R &rhs) { return 0; }
enum { Enum };
int main()
{
Enum * Enum; // causes compilation error
return 0;
}
You can run the code here: http://coliru.stacked-crooked.com/a/f65e333060f40e60
I have defined a concept so-called tuple_like and overloaded operator*() using the concept.
Then, If I multiply enums, my overloaded operator*() for tuple_like is picked up and the compiler complains missing std::tuple_size for enum.
What did I do wrong here and how can I fix it without overload for each class templates - std::tuple and std::pair?
FYI, even if it's unusual, I cannot remove the part of multiplying enums because it's not my code.
Turn tuple_size_v into tuple_size::value to enable SFINAE
template<tuple_like L, tuple_like R, int N = std::tuple_size<L>::value>
auto operator*(const L &lhs, const R &rhs) { return 0; }
However, I don't see any value in declaring an extra template parameter N here. The type that satisfies tuple_like already guarantees that tuple_size_v is a valid value.
It would be more appropriate to move N into the function body
template<tuple_like L, tuple_like R>
auto operator*(const L &lhs, const R &rhs) {
constexpr auto N = std::tuple_size_v<L>; // guaranteed to work
// uses N below
}
Looks like gcc can't handle SFINAE for default values of template arguments. This workaround fixes it:
template<tuple_like L, tuple_like R, int N>
auto operator*(const L &lhs, const R &rhs) { return 0; }
template<tuple_like L, tuple_like R>
auto operator*(const L &lhs, const R &rhs) { return operator*<L, R, std::tuple_size_v<L>>(lhs, rhs); }

C++: wrong template function overload selection

I would like to implement some lazy evaluation in my code. Here is a snippet representative of what I would like to do:
#include <Eigen/Dense>
#include <iostream>
template <int N, int M>
struct S {
template <typename T>
S &operator=(const T &src) {
v = src();
return *this;
}
Eigen::Matrix<double, N, M> v;
};
// If I comment this one the code compiles
template <typename Lhs, typename Rhs>
auto operator+(const Lhs &lhs, const Rhs &rhs) {
return [&]() { return lhs() + rhs(); };
}
template <typename Lhs, int N, int M>
auto operator+(const Lhs &lhs, const S<N, M> &s) {
return [&]() { return lhs() + s.v; };
}
template <typename Rhs, int N, int M>
auto operator+(const S<N, M> &s, const Rhs &rhs) {
return [&]() { return s.v + rhs(); };
}
template <int N0, int M0, int N1, int M1>
auto operator+(const S<N0, M0> &s0, const S<N1, M1> &s1) {
return [&]() { return s0.v + s1.v; };
}
int main() {
static constexpr int n = 4;
S<n, n> s0, s1;
S<Eigen::Dynamic, Eigen::Dynamic> s2;
s1.v = 2. * Eigen::Matrix<double, n, n>::Ones();
s2.v = 3. * Eigen::Matrix<double, n, n>::Ones();
s0 = s1 + s2;
std::cout << s0.v << std::endl;
return 0;
}
I got a compilation error which relates to the fact that when implementing s1 + s2, the first overload of the + operator is selected (and if I comment it, the code works). It seems that for the compiler the first one is considered as the more specialized, whereas to my common sense the last one is the more specialized: the first one accepts whatever arguments, whereas the last one only accepts type related to the template class S.
What is the explanation to that? How can I fix this snippet to make it work while keeping the first overload (which will be required for the lazy evaluation of 'nested' expressions)?
Many thanks!
EDIT: from the answer and comments, I now understand that the problem is related to a conflict between the first overload of the + operator in the snippet and the some overload of the + operator in the Eigen library. The simplified snippet following illustrate that:
#include <Eigen/Dense>
#include <iostream>
template <typename Lhs, typename Rhs>
auto operator+(const Lhs &lhs, const Rhs &rhs) {
return [&]() { return lhs() + rhs(); };
}
int main() {
Eigen::Matrix<double, 4, 4> m0, m1, m2;
m2=m0+m1;
return 0;
}
Here, the + operator of the snippet is selected, whereas I expected that one of the Eigen's would be selected... Why is that?
It looks like the first overload is selected, but not for s1 + s2. That selects the 4th overload. However, inside that 4th overload you have s0.v + s1.v;, and .v is not an S<N, M>

How do I use binary arithmetic operators in combination with templates?

I've written a class Number that contains only one attribute: T value. I'm currently learning about templates, so T is the data type. What I want to achieve is doing the following sort of computation.
Number<int>(2) + Number<double>(1.2)
What I have so far can do a operation, but it fails when there are two different datatypes. So far I've written this:
//class template
template<class T>
class Number
{
public:
T value;
Number(T num1)
{
value = num1;
}
Number<T> operator + ( const Number<T> &other) const
{
return Number<decltype(value+other.value)> (value+other.value);
}
};
It only does the arithmic operation when the datatypes are the same:
Questions:
Why does the program only work with the same datatypes?
This can I answer for a part by myself. I use the line:
Number<T> operator + ( const Number<T> &other) const
So if the left handside is of type int. Every T becomes an int. I don't know how I need to change it without getting an error.
What do I need to fix in order to do computations with different
datatypes?
Edit:
A constraint is that the template may contain only one type argument
Besides declaring a friend operator+ with two template parameters as suggested, you can also place a secondary template for the member function operator+, which allows you to do casting plus.
template<typename T>
class Number
{
public:
T value;
Number(const T&num1)
{
value = num1;
}
template <typename X> auto operator + ( const Number<X> &other) const
{
auto c = this->value + other.value;
return Number<decltype(c)> ( c );
}
};
#include <iostream>
int main()
{
Number<int> n{2};
Number<double> a{3.4};
std::cout << (a+n).value << std::endl;
}
Or, you may use a friend function (I think that this is more symbolic consistent.)
template<typename T>
class Number
{
public:
T value;
Number(T num1)
{
value = num1;
}
Number& operator +=( const Number<T> &other)
{
this->value += other.value;
return *this;
}
};
template <typename T1,typename T2> auto operator+(const Number<T1>&a, const Number<T2>&b)
{
auto c = a.value + b.value;
return Number<decltype(c)>( c );
}
#include <iostream>
#include <type_traits>
template <typename T>
struct Number {
Number(T _value = T(0)) : value(_value) {}
template <typename S>
Number(const Number<S>& n) : value(n.value) {}
T value;
template <typename S>
friend Number<S> operator+(const Number<S>& a, const Number<S>& b);
};
template <typename S>
Number<S> operator+(const Number<S>& a, const Number<S>& b) {
return Number<S>{a.value + b.value};
}
int main() {
std::cout << operator+<typename std::common_type<int, double>::type>(Number<int>{1}, Number<double>{1.2}).value << std::endl;
return 0;
}
this is a c++11 implementation. operator+ contains exactly one argument. I hope this is what you want.

Templated functions with distinct definitions based on type

Assume we have different classes (structs) with different member types and counts. For the sake of the example, they are 2D and 3D vectors.
struct i2 {
int a, b;
i(int a, int b): a(a), b(b) {}
};
struct f2 {
float a, b;
f(float a, float b): a(a), b(b) {}
};
struct i3 {
int a, b, c;
i(int a, int b): a(a), b(b), c(c) {}
};
struct f3 {
float a, b, c;
f(float a, float b): a(a), b(b), c(c) {}
};
I would like to implement some related functions in a templated way. Say an addition operator outside the classes, like
template<class Type> Type operator+(const Type& P, const& Type Q)
{ return Type(P.a + Q.b, P.a + P.b); }
Obviously, this won't work with the 3D variants, which would require
template<class Type> Type operator+(const Type& P, const Type& Q)
{ return Type(P.a + Q.a, P.b + P.b, P.c + Q.c); }
Is there a nice way to achieve this without numerous explicit specializations ? And if this uses advanced C++ features, what is the minimum version that will support it ?
In other words, is there a compile-time mechanism to conditionally instantiate the templates based on the argument types ?
Note that I prefer the initial classes to remain non-templated because this would introduce other issues in my design.
You might use SFINAE once you have your traits.
traits can be done in any version of C++, even if it would be easier with more recent version.
Depending how you want to define your traits, you might use something like
template <typename T>
using is_2d_vector = std::disjunction_t<std::is_same<T, i2>, std::is_same<T, f2>>;
template <typename T>
using is_3d_vector = std::disjunction_t<std::is_same<T, i3>, std::is_same<T, f3>>;
// possible alternatives include detection of T::a, T::b, T::c
template<class T, std::enable_if_t<is_2d_vector<T>, int> = 0>
T operator+(const T& lhs, const T& rhs)
{ return T(lhs.a + rhs.a, lhs.b + rhs.b); }
template<class T, std::enable_if_t<is_3d_vector<T>, int> = 0>
T operator+(const T& lhs, const T& rhs)
{ return T(lhs.a + rhs.a, lhs.b + rhs.b, lhs.c + rhs.c); }
But having template class initially seems simpler:
template <typename T>
struct T2 {
T a, b;
T2(T a, T b): a(a), b(b) {}
friend operator +(const T2& lhs, const T2& rhs)
{ return T(lhs.a + rhs.a, lhs.b + rhs.b); }
};
template <typename T>
struct T3 {
T a, b, c;
T3(T a, T b, T c): a(a), b(b), c(c) {}
friend operator +(const T2& lhs, const T2& rhs)
{ return T(lhs.a + rhs.a, lhs.b + rhs.b, lhs.c + rhs.c); }
};
using i2 = T2<int>;
using i3 = T3<int>;
using f2 = T2<float>;
using f3 = T3<float>;
One way to do it I think is to use compile time (static) reflection. There are quite a few of articles online, here are two of them, https://medium.com/#vesko.karaganev/compile-time-reflection-in-c-17-55c14ee8106b
and
https://Frly.sexy/post/basic-reflection-in-pure-cpp17
But I am not sure this is the best way to solve your problem because a better answer would need more concrete details.

operator overloading with multiple templates

template <class T>
class A
{
private:
T m_var;
public:
operator T () const { return m_var; }
........
}
template<class T, class U, class V>
const A<T> operator+ (const U& r_var1, const V& r_var2)
{ return A<T> ( (T)r_var1 + (T)r_var2 ); }
The idea is to overload the + operator once (instead of three) for the cases:
number + A, A + number, A + A (where number is of type T, the same as m_var).
An interesting case would be if m_var is e.g. int and r_var is long.
Any helps would be highly appreciated. Thank you.
The common pattern to achieve what you want is to actually perform it in the opposite direction: provide an implicit conversion from T to the template and only define the operator for the template.
template <typename T>
struct test {
T m_var;
test( T const & t ) : m_var(t) {} // implicit conversion
test& operator+=( T const & rhs ) {
m_var += rhs.m_var;
}
friend test operator+( test lhs, test const & rhs ) { // *
return lhs += rhs;
}
};
// * friend only to allow us to define it inside the class declaration
A couple of details on the idiom: operator+ is declared as friend only to allow us to define a free function inside the class curly braces. This has some advantages when it comes to lookup for the compiler, as it will only consider that operator if either one of the arguments is already a test.
Since the constructor is implicit, a call test<int> a(0); test<int> b = a + 5; will be converted into the equivalent of test<int> b( a + test<int>(5) ); Conversely if you switch to 5 + a.
The operator+ is implemented in terms of operator+=, in a one-liner by taking the first argument by value. If the operator was any more complex this would have the advantage of providing both operators with a single implementation.
The issue with your operator+ is you have 3 template parameters, one for the return type as well as the cast, but there is no way for the compiler to automatically resolve that parameter.
You are also committing a few evils there with casts.
You can take advantage of the that if you define operator+ as a free template function in your namespace it will only have effect for types defined in that namespace.
Within your namespace therefore I will define, using just T and U
template< typename T >
T operator+( const T & t1, const T& t2 )
{
T t( t1 );
t += t2; // defined within T in your namespace
return t;
}
template< typename T, typename U >
T operator+( const T& t, const U& u )
{
return t + T(u);
}
template< typename T, typename U >
T operator+( const U& u, const T& t )
{
return T(u) + t;
}
a + b in general is not covered by this template unless one of the types of a and b is in the namespace where the template was defined.
You should not overload op+ for unrelated types that you know nothing about – this can break perfectly working code that already exists. You should involve your class as at least one of the parameters to the op+ overload.
If you don't want an implicit conversion from T to A<T>, then I would just write out the overloads. This is the clearest code, and isn't long at all, if you follow the "# to #=" overloading pattern:
template<class T>
struct A {
explicit A(T);
A& operator+=(A const &other) {
m_var += other.m_var;
// This could be much longer, but however long it is doesn't change
// the length of the below overloads.
return *this;
}
A& operator+=(T const &other) {
*this += A(other);
return *this;
}
friend A operator+(A a, A const &b) {
a += b;
return a;
}
friend A operator+(A a, T const &b) {
a += A(b);
return a;
}
friend A operator+(T const &a, A b) {
b += A(a);
return b;
}
private:
T m_var;
};
C++0x solution
template <class T>
class A
{
private:
T m_var;
public:
operator T () const { return m_var; }
A(T x): m_var(x){}
};
template<class T,class U, class V>
auto operator+ (const U& r_var1, const V& r_var2) -> decltype(r_var1+r_var2)
{
return (r_var1 + r_var2 );
}
int main(){
A<int> a(5);
a = a+10;
a = 10 + a;
}
Unfortunately changing template<class T,class U, class V> to template<class U, class V> invokes segmentation fault on gcc 4.5.1. I have no idea why?