c++templatespredicatenumeric-limits

Check if numeric type is subset of another


I try to implement a meta function which checks if an integer type is a subset of another integer type. It should be platform independent and work at least with all numeric types defined by the C++ Standard. My current implementation has 5 branches. I have two questions about it.

  1. Do I miss any cases?
  2. Is the 4th branch necessary?
template <typename T1, typename T2>
constexpr bool is_subset(T1, T2)
{
    if constexpr (std::is_same_v<T1, T2>)
    {
        return true;
    }
    if constexpr (std::is_same_v<T1, std::uintmax_t>)
    {
        return false;
    }
    else if constexpr (std::is_same_v<T1, std::intmax_t>)
    {
        return false;
    }
    else if constexpr (std::is_unsigned_v<T1> && std::is_unsigned_v<T2>)
    {
        return static_cast<std::uintmax_t>(std::numeric_limits<T1>::min()) >= static_cast<std::uintmax_t>(std::numeric_limits<T2>::min()) &&
               static_cast<std::uintmax_t>(std::numeric_limits<T1>::max()) <= static_cast<std::uintmax_t>(std::numeric_limits<T2>::max())
    }
    else
    {
        return static_cast<std::intmax_t>(std::numeric_limits<T1>::min()) >= static_cast<std::intmax_t>(std::numeric_limits<T2>::min()) &&
               static_cast<std::intmax_t>(std::numeric_limits<T1>::max()) <= static_cast<std::intmax_t>(std::numeric_limits<T2>::max())
    }
}

Fixed implementation for intmax_t == long:

template <typename T1, typename T2>
constexpr bool is_subset2(T1, T2) noexcept
{
    if constexpr (sizeof(T1) == sizeof(T2) && std::is_signed_v<T1> == std::is_signed_v<T2>)
    {
        return true;
    }      
    else if constexpr (sizeof(T1) == sizeof(std::intmax_t))
    {
        return false;
    }   
    else if constexpr (std::is_unsigned_v<T1> && std::is_unsigned_v<T2>)
    {
        return static_cast<std::uintmax_t>(std::numeric_limits<T1>::min()) >= static_cast<std::uintmax_t>(std::numeric_limits<T2>::min()) &&
               static_cast<std::uintmax_t>(std::numeric_limits<T1>::max()) <= static_cast<std::uintmax_t>(std::numeric_limits<T2>::max());
    }
    else
    {
        return static_cast<std::intmax_t>(std::numeric_limits<T1>::min()) >= static_cast<std::intmax_t>(std::numeric_limits<T2>::min()) &&
               static_cast<std::intmax_t>(std::numeric_limits<T1>::max()) <= static_cast<std::intmax_t>(std::numeric_limits<T2>::max());
    }
}

Solution

  • I don't know why you have separate cases for std::uintmax_t and std::intmax_t. I think it can be simplified to only 3 cases:

    template <typename T1, typename T2>
    constexpr bool is_subset2(T1, T2) noexcept
    {
        if constexpr (sizeof(T1) == sizeof(T2))
        {
            // true if both signed or both unsigned
            return std::is_signed_v<T1> == std::is_signed_v<T2>;
        }
        else if constexpr (sizeof(T1) < sizeof(T2))
        {
            // true if both unsigned, or T2 is signed
            return std::is_signed_v<T2> || std::is_unsigned_v<T1>;
        }
        else
        {
            return false;
        }
    }