sticky question regarding boost rational
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
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 <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.
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 <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.
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
participants (2)
-
Marc Glisse
-
Robert Ramey