Reputation: 5043
How do you create a generic num like class so that the only thing that matters is that the relevant member function/operator exists?
I've read up on SFINAE but I don't get it to be honest.
#include <iostream>
template<typename T>
class Numeric {
const T a;
public:
Numeric(const T &v) : a(v) {}
T operator+(const Numeric<T> &b) {
return a + b.a;
}
};
int main() {
Numeric<float> fl1(35.5);
Numeric<float> fl2(10.5);
Numeric<uint64_t> i64(10000);
std::cout << (i64 + fl1 + fl2) << std::endl;
return 0;
}
Here, fl1 + fl2
would be fine but since the operator definition says T
is the same type i64
can't be mixed with fl1
or fl2
.
Are templates the right thing here, would it be better to use the object hierarchy defining a top level Num
for e.g. that defines all the operators and have a sub class for each supported type? Though I don't think that solves the mixing types problem.
EDIT 1: Background in res to Barry/lisyarus: I'm changing an old code base which has a type defined like this:
template<typename a> struct NumT : public SomeSuperType<a> { mp::cpp_int val;};
The change is to add support for native float
and double
types in this NumT
type in a transparent a way as possible.
Ideally NumT shouldn't change beyond changing the decl. type of val
.
Existing code just does a + b
and other arithmetic operations on val
, not breaking the existing API stuff is important.
i64 + fl1 + fl2 == 10046
Upvotes: 1
Views: 128
Reputation: 21576
There's a reason why numeric types are different (int
, float
and so on), trying to commonize them can get awry. Nonetheless, you can use std::common_type
to deduce the type that can contain both.
Mind you there will be a loss of "precision" here.. For example, if you have an unsigned long long
of 1434263462343574573ULL
, converting it to double
will lose some significant digits.
#include <iostream>
#include <type_traits>
template<typename T>
class Numeric {
const T a;
public:
Numeric(const T &v) : a(v) {}
T get() const { return a; }
};
template<typename T, typename U>
Numeric<typename std::common_type<T, U>::type> //With C++14, do std::common_type_t<T, U>
operator + (const Numeric<T>& a, const Numeric<U>& b) {
return a.get() + b.get(); //Works because of the converting constructor
}
template<typename T>
std::ostream& operator << (std::ostream& os, const Numeric<T>& n){
os << n.get();
return os;
}
int main() {
Numeric<float> fl1(35.5);
Numeric<float> fl2(10.5);
Numeric<uint64_t> i64(10000);
std::cout << (i64 + fl1 + fl2) << std::endl;
return 0;
}
This prints:
10046
Upvotes: 2