如何在 c++03 中获得模板参数的最精确表示?

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

下面的代码按预期工作(打印出 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;
}

但是,它需要 C++11。假设我仅限于 C++03,是否有可能实现相同的行为? (即:使 operator+ 的 return 类型使用成员 n_ 的最精确表示?)

你可以使用 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_;
}

或者根据usual arithmetic conversions的规则为此编写自己的trait。我希望这个是令人满意的:

// 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 > {};

它至少比 Boost 的更具可读性。为简洁起见,它依赖于 TR1 中的 is_floating_pointis_signedis_unsigned,但如果您不能使用 TR1,这些很容易自己实现。

DEMO

N.B。当 AB 是同一类型且排名低于 int 时,它不会给出与 std::common_type 相同的结果。这是设计使然。它给出与 decltype(A{} + B{}) 相同的结果。当我意识到这一点时,我应该给它起一个不同的名字,但我不会被打扰。