I'd like to have a function to check whether a pointer points to an element of a vector:
template <typename T>
bool pointsToElement(const std::vector<T>& vec, const T* ptr);
The function is a helper function for safely creating an iterator from a pointer:
template <typename T>
std::vector<T>::iterator toIterator(std::vector<T>& vec, T* ptr)
{
assert(pointsToElement(vec, ptr));
return vec.begin() + (ptr - &vec[0]);
}
The "obvious" way would be something like:
template <typename T>
bool pointsToElement(const std::vector<T>& vec, const T* ptr)
{
if (vec.empty())
return false;
return ptr >= &vec.front() && ptr <= &vec.back();
}
Unfortunately, this seems to invoke undefined behavior, since it may compare pointers to different objects.
A safe way would be:
template <typename T>
bool pointsToElement(const std::vector<T>& vec, const T* ptr)
{
for (auto& elem : vec) {
if (&elem == ptr)
return true;
}
return false;
}
But this of course is O(N), so very undesirable.
I can imagine one other way:
template <typename T>
bool pointsToElement(const std::vector<T>& vec, const T* ptr)
{
if (vec.empty())
return false;
intptr_t pos = reinterpret_cast<intptr_t>(ptr);
intptr_t begin = reinterpret_cast<intptr_t>(&vec.front());
intptr_t end = reinterpret_cast<intptr_t>(&vec.back());
return pos >= begin && pos <= end;
}
I don't think the standard guarantees any ordering on inptr_t, but this should at least not invoke any undefined behavior and produce the correct results on major platforms (I'm only concerned about Linux and Windows at the moment).
Is this analysis correct? Is there any better way to check whether a pointer falls within a certain range?
(Note: there are a few similar questions, but none considers the possibility of using a cast to intptr_t).