On Sat, 31 Oct 2020, Robert Ramey via Boost wrote:
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.
So you are multiplying 2 rationals, you could have made the example more minimal with sd * sd.
rational.hpp contains - among other things, the following definitions for the * operator:
template
BOOST_CXX14_CONSTEXPR inline typename boost::enable_if_c < rational_detail::is_compatible_integer ::value || is_same ::value, rational<IntType> >::type operator * (const rational<IntType>& a, const Arg& b) { rational<IntType> t(a); return t *= b; } template BOOST_CXX14_CONSTEXPR inline typename boost::enable_if_c < rational_detail::is_compatible_integer ::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.
They look very different. The first one is for rational * integer or rational * rational, and the second one for integer * rational.
the definition for is_compatible_integer.
namespace rational_detail{
template
struct is_compatible_integer; template
struct is_compatible_integer ::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 ::value) || is_same ::value) || (is_class<ToInt>::value && is_class<FromInt>::value && is_convertible ::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.
AFAICT, Arg is rational in your case, so it is **not** the same as IntType. And I hope that rational is not convertible to safe<int>, although a badly constrained constructor there could easily cause trouble. -- Marc Glisse