zcourts
zcourts

Reputation: 5043

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

Upvotes: 1

Views: 128

Answers (1)

WhiZTiM
WhiZTiM

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

Related Questions