2013-05-25 17 views
12

2つの一般的に予期しない(異なる)型の2つの整数を比較する完全型保証型と最も柔軟な(constexprの点で)方法は何ですか?ここで任意の型の整数の比較

+3

はい^ –

+1

私にコードレビューのためのより良いフィットだ - あなたの場合このための単体テストを作成し、予想外の動作を起こした場合、これは適切な場所になります。 –

+1

['std :: common_type'](http://en.cppreference.com/w/cpp/types/common_type)を参照してください。それは比較の前に両側をキャストできる型を与えます。 – cHao

答えて

12

はアイデアです:ちょうど比較し、両方のタイプが符号なしの場合は

  • :私たちは、「通常の算術変換」を使用する必要があります。

  • 両方の型が署名されている場合は、単に比較するだけです。

  • 署名の度合いが異なり署名付きの値が負の場合は完了です。

  • 実際の作業は、両方の値が負ではなく、符号が異なる場合に適用されます。符号なしの値が符号付きの型の最大符号付きの値より大きい場合、処理は完了です。それ以外の場合、符号なしの値は値を変更せずに符号付きの型に変換して比較することができます。

ここでの試みです:

#include <type_traits> 
#include <limits> 

template <bool SameSignedness> struct IntComparerImpl; 

template <typename T, typename U> 
constexpr bool IntCompare(T x, U y) 
{ 
    return IntComparerImpl<std::is_signed<T>::value == 
          std::is_signed<U>::value>::compare(x, y); 
} 

// same signedness case: 
template <> struct IntComparerImpl<true> 
{ 
    template<typename T, typename U> 
    static constexpr bool compare(T t, U u) 
    { 
     return t < u; 
    } 
}; 

// different signedness case: 
template <> struct IntComparerImpl<false> 
{ 
    // I1 is signed, I2 is unsigned 
    template <typename I1, typename I2> 
    static constexpr typename std::enable_if<std::is_signed<I1>::value, bool>::type 
    compare(I1 x, I2 y) 
    { 
     return x < 0 
      || y > std::numeric_limits<I1>::max() 
      || x < static_cast<I1>(y); 
    } 

    // I1 is unsigned, I2 is signed 
    template <typename I1, typename I2> 
    static typename std::enable_if<std::is_signed<I2>::value, bool>::type 
    compare(I1 x, I2 y) 
    { 
     return !(y < 0) 
      || !(x > std::numeric_limits<I2>::max()) 
      || static_cast<I2>(y) < x; 
    } 
}; 
+0

「符号なしの値が符号付きの型の最大符号付き値より大きい場合」と思われますが、ここでは何らかの論理再帰があります。 – Orient

+0

"符号付きの型の最大符号付き値"と符号なし値 – Orient

+0

の比較についての注意点@Dukales:どのように?符号なし整数200と符号付き文字50を比較しているとします.200が最大符号付き文字(127)よりも大きいので、これで完了です。 –

1

My own solutionは(N3485.pdf§5に基づいて)これです:

#include <type_traits> 
#include <limits> 
#include <utility> 

#include <cstdint> 
#include <cstdlib> 

template< typename L, typename R > 
inline constexpr 
typename std::enable_if< (std::is_signed<L>::value && !std::is_signed<R>::value), bool >::type 
less(L const & lhs, R const & rhs) 
{ 
    static_assert(std::is_integral<L>::value, 
        "lhs value must be of integral type"); 
    static_assert(std::is_integral<R>::value, 
        "rhs value must be of integral type"); 
    using T = typename std::common_type< L, R >::type; 
    return (lhs < static_cast<L>(0)) || (static_cast< T const & >(lhs) < static_cast< T const & >(rhs)); 
} 

template< typename L, typename R > 
inline constexpr 
typename std::enable_if< (!std::is_signed<L>::value && std::is_signed<R>::value), bool >::type 
less(L const & lhs, R const & rhs) 
{ 
    static_assert(std::is_integral<L>::value, 
        "lhs value must be of integral type"); 
    static_assert(std::is_integral<R>::value, 
        "rhs value must be of integral type"); 
    using T = typename std::common_type< L, R >::type; 
    return !(rhs < static_cast<R>(0)) && (static_cast< T const & >(lhs) < static_cast< T const & >(rhs)); 
} 

template< typename L, typename R > 
inline constexpr 
typename std::enable_if< (std::is_signed<L>::value == std::is_signed<R>::value), bool >::type 
less(L const & lhs, R const & rhs) 
{ 
    static_assert(std::is_integral<L>::value, 
        "lhs value must be of integral type"); 
    static_assert(std::is_integral<R>::value, 
        "rhs value must be of integral type"); 
    return lhs < rhs; 
} 

namespace 
{ 

static_assert(less(1, 2), "0"); 
static_assert(less(-1, std::numeric_limits<std::uintmax_t>::max()), "1"); 
static_assert(less< std::int8_t, std::uintmax_t >(-1, std::numeric_limits<std::uintmax_t>::max()), "2"); 
static_assert(less< std::intmax_t, std::uint8_t >(-1, std::numeric_limits<std::uint8_t>::max()), "3"); 
#pragma GCC diagnostic push 
#pragma GCC diagnostic ignored "-Wsign-compare" 
static_assert(!(-1 < std::numeric_limits< unsigned long >::max()), "4"); 
#pragma GCC diagnostic pop 
static_assert(less(-1, std::numeric_limits< unsigned long >::max()), "5"); 

} 

int main() 
{ 
     return EXIT_SUCCESS; 
} 
関連する問題