The simplest trick is to rely on overload resolution which already defines its rules of precedence. In the below solution, with an additional argument 0
, 0 -> int
is better than 0 -> char
, hence, the former will be the preferred one if not excluded by an expression SFINAE, and the latter still viable for a fallback call.
#include <utility>
template <typename T, typename U>
auto smart_division_impl(T a, U b, int)
-> decltype(a/b)
{
return a/b;
}
template <typename T, typename U>
auto smart_division_impl(T a, U b, char)
-> decltype(a * (U(1)/b))
{
return a * (U(1)/b);
}
template <typename T, typename U>
auto smart_division(T&& a, U&& b)
-> decltype(smart_division_impl(std::forward<T>(a), std::forward<U>(b), 0))
{
return smart_division_impl(std::forward<T>(a), std::forward<U>(b), 0);
}
DEMO
If you had more overloads, you could instead introduce a helper type to prioritize each one:
template <int I> struct rank : rank<I-1> {};
template <> struct rank<0> {};
template <typename T, typename U>
auto smart_division_impl(T a, U b, rank<2>) -> decltype(a/b)
// ~~~~~~^ highest priority
{
return a/b;
}
template <typename T, typename U>
auto smart_division_impl(T a, U b, rank<1>) -> decltype(a * (U(1)/b))
// ~~~~~~^ mid priority
{
return a * (U(1)/b);
}
template <typename T, typename U>
int smart_division_impl(T a, U b, rank<0>)
// ~~~~~~^ lowest priority
{
return 0;
}
template <typename T, typename U>
auto smart_division(T&& a, U&& b)
-> decltype(smart_division_impl(std::forward<T>(a), std::forward<U>(b), rank<2>{}))
{
return smart_division_impl(std::forward<T>(a), std::forward<U>(b), rank<2>{});
}
DEMO 2
Here again, rank<2> -> rank<2>
is better than rank<2> -> rank<1>
which in turn is preferred to rank<2> -> rank<0>