Not a real meerkat
Not a real meerkat

Reputation: 5739

How can I get the most precise representation of a template parameter in c++03?

The code bellow works as expected(it prints out 2.1):

#include <iostream>

template<typename T>
struct number {
  T n_;
  number(T n)
   : n_{n}
  {}
};

template<typename A, typename B>
auto operator+(number<A> a, number<B> b) -> number<decltype(a.n_+b.n_)> {
  return a.n_+b.n_;
}

int main() {
  number<int> a{1};
  number<double> b{1.1};
  number<double> c{a+b};
  std::cout << c.n_ << std::endl;
}

However, it requires C++11. Assuming I'm restricted to C++03, is it possible to achieve the same behaviour? (i.e.: Make the return type of operator+ use the most precise representation for member n_?)

Upvotes: 0

Views: 147

Answers (1)

Oktalist
Oktalist

Reputation: 14714

You could use Boost.TypeTraits:

template <typename A, typename B>
typename boost::common_type<A, B>::type operator+(number<A> a, number<B> b) {
  return a.n_ + b.n_;
}

Or write your own trait for this purpose based on the rules of the usual arithmetic conversions. I hope this one is satisfactory:

// 1. A and B undergo integral promotions.
// 2. If the rank of B is higher than A then they are swapped.
// 3. The result is A, if:
//     i. A or B or both are floating point, or
//     ii. A and B have the same signedness, or
//     iii. A is unsigned, or
//     iv. The size of A is greater than the size of B.
// 4. Otherwise, the result is make_unsigned<A>.

namespace detail {
    using namespace std::tr1;

    template <typename T> struct promote { typedef T type; };
    template <> struct promote<bool> { typedef int type; };
    template <> struct promote<char> { typedef int type; };
    template <> struct promote<signed char> { typedef int type; };
    template <> struct promote<unsigned char> { typedef int type; };
    template <> struct promote<short> { typedef int type; };
    template <> struct promote<unsigned short> { typedef int type; };

    template <typename> struct rank;
    template <> struct rank<int> { enum { value = 0 }; };
    template <> struct rank<unsigned> { enum { value = 0 }; };
    template <> struct rank<long> { enum { value = 1 }; };
    template <> struct rank<unsigned long> { enum { value = 1 }; };
    template <> struct rank<long long> { enum { value = 2 }; };
    template <> struct rank<unsigned long long> { enum { value = 2 }; };
    template <> struct rank<float> { enum { value = 3 }; };
    template <> struct rank<double> { enum { value = 4 }; };
    template <> struct rank<long double> { enum { value = 5 }; };

    template <typename> struct make_unsigned;
    template <> struct make_unsigned<int> { typedef unsigned type; };
    template <> struct make_unsigned<long> { typedef unsigned long type; };
    template <> struct make_unsigned<long long> { typedef unsigned long long type; };

    // 4.
    template < typename A
             , typename B
             , bool Is_floating_point_or_same_signs_or_A_is_unsigned_or_bigger >
    struct common_type_impl {
        typedef A type;
    };
    template <typename A, typename B>
    struct common_type_impl<A, B, false>
         : make_unsigned<A> {};

    // 3.
    template <typename A, typename B, bool A_is_higher>
    struct common_type_swap
         : common_type_impl< A
                           , B
                           , is_floating_point<A>::value || is_floating_point<B>::value
                             || (is_signed<A>::value == is_signed<B>::value)
                             || is_unsigned<A>::value || (sizeof(A) > sizeof(B))
                           > {};
    template <typename A, typename B>
    struct common_type_swap<A, B, false>
         : common_type_swap<B, A, true> {};

    // 2.
    template <typename A, typename B>
    struct common_type
         : common_type_swap<A, B, (rank<A>::value > rank<B>::value)> {};
}

// 1.
template <typename A, typename B>
struct common_type
     : detail::common_type< typename detail::promote<A>::type
                          , typename detail::promote<B>::type > {};

It is at least more readable than Boost's. For brevity it relies on is_floating_point, is_signed, and is_unsigned from TR1, but these are easy to implement yourself if you can't use TR1.

DEMO

N.B. It doesn't give the same result as std::common_type when A and B are the same type and ranked below int. This is by design. It gives the same result as decltype(A{} + B{}). I should've given it a different name when I realized this but I couldn't be bothered.

Upvotes: 1

Related Questions