如何在 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_point
、is_signed
和 is_unsigned
,但如果您不能使用 TR1,这些很容易自己实现。
N.B。当 A
和 B
是同一类型且排名低于 int
时,它不会给出与 std::common_type
相同的结果。这是设计使然。它给出与 decltype(A{} + B{})
相同的结果。当我意识到这一点时,我应该给它起一个不同的名字,但我不会被打扰。
下面的代码按预期工作(打印出 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_point
、is_signed
和 is_unsigned
,但如果您不能使用 TR1,这些很容易自己实现。
N.B。当 A
和 B
是同一类型且排名低于 int
时,它不会给出与 std::common_type
相同的结果。这是设计使然。它给出与 decltype(A{} + B{})
相同的结果。当我意识到这一点时,我应该给它起一个不同的名字,但我不会被打扰。