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));
}