Currently, Range
assumes that the difference between two ValueType
s is still a ValueType
.
That certainly is true for all primitive numeric types (int
, float
, etc.), but there are cases where that is not the case. One such example is the Time
class in JUCE, where:
Time - Time -> RelativeTime
And yet a Range <Time>
object would totally make sense: it would work out of the box for all the functionality to check if another Time
object is within range, etc.
That breaks where Range
tries to fit the difference of two Time
objects in another Time
object, for example:
constexpr inline ValueType getLength() const noexcept { return end - start; }
what I propose is to add a second DistanceType
parameter to the Range
, which defaults to the type that results as the difference between two ValueType
s.
template <typename ValueType,
typename DistanceType = decltype (ValueType() - ValueType ())>
class Range
...
and use it as the type of the differences between ValueTypes (as in getLength() above) and as the type of the offsets to be applied to ValueTypes, for example:
[[nodiscard]] constexpr Range expanded (DistanceType amount) const noexcept
{
return Range (start - amount, end + amount);
}
None of the expressions currently used in Range need any change. The only change is in the type of some parameters and return values.
To make things safer, the required relationship between ValueType and DistanceType can be formalized with two static asserts:
// requirement #1: ValueType - ValueType -> DistanceType
static_assert (std::is_convertible <decltype (ValueType () - ValueType()), DistanceType>::value);
// requirement #2: ValueType + DistanceType -> ValueType
static_assert (std::is_convertible <decltype (ValueType () + DistanceType()), ValueType>::value);