Consider the following code:
template<typename T, typename U>
constexpr auto divide(T rhs, U lhs) { return rhs / static_cast<T>(lhs); }
Here divide defaults to the rhs type (which is the same default behavior we get from the compiler.
But what if we wanted to make divide smarter such that it would deduce in compile time the best type to return (i.e. the one which would not cause narrowing cast of any argument).
So divide(2, 3.0) would result in 0.6666... and so would divide(2.0, 3), but divide(2, 3) would still result in 0.
something like
template<typename T, typename U>
constexpr auto divide2(T rhs, U lhs) {
if constexpr (is_same<U, double>::value)
return static_cast<double>(rhs) / lhs;
else
return rhs / static_cast<T>(lhs);
}
But more generic - I could go over each possible type, but is there a better way?
Is there some smart use of concepts or a metaprogramming method to figure out which is the wider type and choose it as the cast type? something like max_type(T, U)
?
CodePudding user response:
I think you are looking for std::common_type
:
#include <type_traits>
#include <iostream>
template<typename T>
constexpr T divide_impl(T rhs, T lhs) { return rhs /lhs; }
template<typename T, typename U>
constexpr auto divide(T rhs, U lhs) { return divide_impl<std::common_type_t<T,U>>(rhs,lhs); }
int main ()
{
std::cout << std::is_same_v<int, decltype( divide(2,2))> << "\n";
std::cout << std::is_same_v<double, decltype( divide(2.0,2))> << "\n";
}
Output is
1
1
because divide(2,2)
returns an int
while divide(2.0,2)
returns a double
.
However, ....
Here divide defaults to the rhs type (which is the same default behavior we get from the compiler.
This is not right. See here for arithmetic conversions: https://en.cppreference.com/w/c/language/conversion. And as mentioned in a comment, no cast is actually needed. The above is only useful in the more general case, but you wouldn't do such things for rhs / lhs
.
CodePudding user response:
Using common_type_t
, and constexpr
.
#include <type_traits>
template<typename type1_t, typename type2_t>
constexpr auto divide(const type1_t& value1, const type2_t& value2)
{
using type_t = std::common_type_t<type1_t, type2_t>;
return static_cast<type_t>(value1) / static_cast<type_t>(value2);
}
int main()
{
static_assert(0.5 == divide(1.0, 2));
static_assert(3ul == divide(10, 3ul));
static_assert(0.5f == divide(1.0, 2.0f));
}