The templates are the basics, you just need SFINAE.
#include <limits> #include <utility> template <typename T> struct is_integral { static bool const value = std::numeric_limits<T>::is_integer; }; template <typename Integral, typename T> typename std::enable_if<is_integral<Integral>::value, bool>::type inRange(Integral x, T start, T end) { return x >= static_cast<Integral>(start) and x <= static_cast<Integral>(end); } template <typename Real, typename T> typename std::enable_if<not is_integral<Real>::value, bool>::type inRange(Real x, T start, T end) { return x >= static_cast<Real>(start) and x <= static_cast<Real>(end); }
In theory, we could be even softer and just let start and end have different types. If we want.
EDIT : Modified to upgrade to the real version as soon as there is one real version with built-in health check.
#include <limits> #include <utility> #include <iostream> template <typename T> struct is_integral { static bool const value = std::numeric_limits<T>::is_integer; }; template <typename T> struct is_real { static bool const value = not is_integral<T>::value; }; template <typename T, typename L, typename R> struct are_all_integral { static bool const value = is_integral<T>::value and is_integral<L>::value and is_integral<R>::value; }; template <typename T, typename L, typename R> struct is_any_real { static bool const value = is_real<T>::value or is_real<L>::value or is_real<R>::value; }; template <typename T, typename L, typename R> typename std::enable_if<are_all_integral<T, L, R>::value, bool>::type inRange(T x, L start, R end) { typedef typename std::common_type<T, L, R>::type common; std::cout << " inRange(" << x << ", " << start << ", " << end << ") -> Integral\n"; return static_cast<common>(x) >= static_cast<common>(start) and static_cast<common>(x) <= static_cast<common>(end); } template <typename T, typename L, typename R> typename std::enable_if<is_any_real<T, L, R>::value, bool>::type inRange(T x, L start, R end) { typedef typename std::common_type<T, L, R>::type common; std::cout << " inRange(" << x << ", " << start << ", " << end << ") -> Real\n"; return static_cast<common>(x) >= static_cast<common>(start) and static_cast<common>(x) <= static_cast<common>(end); } int main() { std::cout << "Pure cases\n"; inRange(1, 2, 3); inRange(1.5, 2.5, 3.5); std::cout << "Mixed int/unsigned\n"; inRange(1u, 2, 3); inRange(1, 2u, 3); inRange(1, 2, 3u); std::cout << "Mixed float/double\n"; inRange(1.5f, 2.5, 3.5); inRange(1.5, 2.5f, 3.5); inRange(1.5, 2.5, 3.5f); std::cout << "Mixed int/double\n"; inRange(1.5, 2, 3); inRange(1, 2.5, 3); inRange(1, 2, 3.5); std::cout << "Mixed int/double, with more doubles\n"; inRange(1.5, 2.5, 3); inRange(1.5, 2, 3.5); inRange(1, 2.5, 3.5); }
Run ideone :
Pure cases inRange(1, 2, 3) -> Integral inRange(1.5, 2.5, 3.5) -> Real Mixed int/unsigned inRange(1, 2, 3) -> Integral inRange(1, 2, 3) -> Integral inRange(1, 2, 3) -> Integral Mixed float/double inRange(1.5, 2.5, 3.5) -> Real inRange(1.5, 2.5, 3.5) -> Real inRange(1.5, 2.5, 3.5) -> Real Mixed int/double inRange(1.5, 2, 3) -> Real inRange(1, 2.5, 3) -> Real inRange(1, 2, 3.5) -> Real Mixed int/double, with more doubles inRange(1.5, 2.5, 3) -> Real inRange(1.5, 2, 3.5) -> Real inRange(1, 2.5, 3.5) -> Real