Reputation: 5739
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
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.
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