Шаблоны являются основой здесь, вам просто нужно немного 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);
}
Теоретически, мы можем быть даже больше снисходительными и просто позволить start
и end
есть разные типы.Если мы хотим.
EDIT : Изменено переключение на реальную версию, как только есть одна реальная, со встроенной проверкой работоспособности.
#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);
}
Запустить на 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