zcourts zcourts - 4 months ago 9
C++ Question

Generic Num class

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

Answer

There's a reason why numeric types are in classes, 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
Comments