I want to write a function which perform a division between two arguments a
and b
of different type, using the expression a/b
if the division operator is defined, or fall back in the a * (1/b)
if there is no such operator.
This is what i thought of, but i don't know how to disable the second definition (or prioritize the first) when both *
and /
operators are defined:
template<typename T, typename U>
auto smart_division(T a, U b) -> decltype (a/b) {
return a/b;
}
template<typename T, typename U>
auto smart_division(T a, U b) -> decltype (a * (U(1)/b)) {
return a * (U(1)/b);
}
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>
You should make one of options preferable if both can compile. For example:
#include <iostream>
template<typename T, typename U>
auto helper(T a, U b, int) -> decltype (a/b) {
std::cout << "first";
return a/b;
}
template<typename T, typename U>
auto helper(T a, U b, ...) -> decltype (a * (U(1)/b)) {
std::cout << "second";
return a * (U(1)/b);
}
template<typename T, typename U>
auto smart_division(T a, U b) -> decltype (helper(a, b)) {
return helper(a, b, 0);
}
struct Test {
explicit Test(int) {}
};
int operator / (Test a, Test b) {
return 1;
}
int main() {
std::cout << smart_division(1.0, 2.0);
Test t{5};
std::cout << smart_division(1, t);
return 0;
}
Here if there is no division available, the second function is the only available funciton. If division is available, then there are 2 functions:
helper(T, U, int)
and helper(T, U, ...)
and the first one is better match for call helper(t, u, 1)
DEMO
Note, that you may want to use perfect forwarding in smart_division, I skipped it for clarity
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With