The only correct way to do this is an approach like this.
template <typename T>
bool points_within_array(T* p, T* begin, T* end)
{
for (; begin != end; ++begin)
{
if (p == begin)
return true;
}
return false;
}
Fairly obviously, this doesn't work if T == void
. I'm not sure whether two void*
technically define a range or not. Certainly if you had Derived[n]
, it would be incorrect to say that (Base*)Derived, (Base*)(Derived + n)
defined a valid range so I can't see it being valid to define a range with anything other than a pointer to the actual array element type.
The method below fails because it is unspecified what <
returns if the two operands don't point to members of the same object or elements of the same array. (5.9 [expr.rel] / 2)
template <typename T>
bool points_within_array(T* p, T* begin, T* end)
{
return !(p < begin) && (p < end);
}
The method below fails because it is also unspecified what std::less<T*>::operator()
returns if the two operands don't point to members of the same object or elements of the same array.
It is true that a std::less
must be specialized for any pointer type to yield a total order if the built in <
does not but this is only useful for uses such as providing a key for a set
or map
. It is not guaranteed that the total order won't interleave distinct arrays or objects together.
For example, on a segmented memory architecture the object offset could be used for <
and as the most significant differentiator for std::less<T*>
with the segment index being used to break ties. In such a system an element of one array could be ordered between the bounds of a second distinct array.
template <typename T>
bool points_within_array(T* p, T* begin, T* end)
{
return !(std::less<T*>()(p, begin)) && (std::less<T*>()(p, end));
}