$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
From: Robert Ramey (ramey_at_[hidden])
Date: 2020-10-31 18:40:08
boost rational is tripping up one of my examples in safe numerics:
     // solution: use safe integer in rational definition
     using safe_rational = boost::rational<
         boost::safe_numerics::safe<int>
     >;
     // use rationals created with safe_t
     const safe_rational sc {1, std::numeric_limits<int>::max()};
     std::cout << "c = " << sc << std::endl;
     const safe_rational sd {1, 2};
     std::cout << "d = " << sd << std::endl;
     std::cout << "c * d = ";
     try {
         // multiply them. This will overflow
         std::cout << sc * sd << std::endl;   // use of overload 
operator * is ambiguous.
rational.hpp contains - among other things, the following definitions 
for the * operator:
template <class IntType, class Arg>
BOOST_CXX14_CONSTEXPR
inline typename boost::enable_if_c <
    rational_detail::is_compatible_integer<Arg, IntType>::value || 
is_same<rational<IntType>, Arg>::value, rational<IntType> >::type
    operator * (const rational<IntType>& a, const Arg& b)
{
       rational<IntType> t(a);
       return t *= b;
}
template <class Arg, class IntType>
BOOST_CXX14_CONSTEXPR
inline typename boost::enable_if_c <
    rational_detail::is_compatible_integer<Arg, IntType>::value, 
rational<IntType> >::type
    operator * (const Arg& b, const rational<IntType>& a)
{
       rational<IntType> t(a);
       return t *= b;
}
Soooooo - it seems that sc * sd will match both of the above 
definitions.  Its unclear what the purpose of these two different 
overloads are.  They look pretty similar to me.
the definition for is_compatible_integer.
namespace rational_detail{
    template <class FromInt, class ToInt, typename Enable = void>
    struct is_compatible_integer;
    template <class FromInt, class ToInt>
    struct is_compatible_integer<FromInt, ToInt, typename 
enable_if_c<!is_array<FromInt>::value>::type>
    {
       BOOST_STATIC_CONSTANT(bool, value = 
((std::numeric_limits<FromInt>::is_specialized
        && std::numeric_limits<FromInt>::is_integer
          && (std::numeric_limits<FromInt>::digits 
<=std::numeric_limits<ToInt>::digits)
          && (std::numeric_limits<FromInt>::radix == 
std::numeric_limits<ToInt>::radix)
          && ((std::numeric_limits<FromInt>::is_signed == false) || 
(std::numeric_limits<ToInt>::is_signed == true))
          && is_convertible<FromInt, ToInt>::value)
          || is_same<FromInt, ToInt>::value)
          || (is_class<ToInt>::value && is_class<FromInt>::value && 
is_convertible<FromInt, ToInt>::value));
    };
...
In fact, since it looks like if FromInt and ToInt are the same types (as 
they are in my case, one will always got more than one match.  Is anyone 
(John Maddock - where are you?) able to shed some light on this for me?
Robert Ramey