问题
Intuitively to check whecker pointer p
lies in [a
,b
) one will do
a<=p && p<b
However, comparing pointers from two arrays results in unspecified behavior and thus we cannot safely say p
is in [a
,b
) from this comparison.
Is there any way one can check for this with certainty?
(It would be better if it can be done for std::vector<T>::const_iterator
, but I don't think it's feasible.)
回答1:
Here's a partial solution. You can leverage the fact that the comparison would invoke unspecified behavior, and the fact that a core-constant-expression can't perform this operation:
template<typename T>
constexpr bool check(T *p, T *a, T *b)
{
return a <= p and p < b;
}
Now this function can be used like this:
int main()
{
int arr[5];
int arr_2[5];
constexpr bool b1 = check(arr + 1, arr, arr + 3); // ok
constexpr bool b2 = check(arr_2 + 1, arr, arr + 3); // error
}
Here's a demo.
This obviously works only if the pointer values are known at compile time. At run-time, there is no efficient way of doing this check.
回答2:
If I understand you correctly, you want to check if vector iterator is between two other vector iterators.
Then you may use std::distance to compute distance between vector.begin and a, p and b and then simply compare itegers you get from distance return value.
回答3:
The solution for pointers is to use the comparison objects defined in <functional>
, like less
/less_equal
, etc.
From §20.8.5/8 of the c++17 standard1:
For templates
greater
,less
,greater_equal
, andless_equal
, the specializations for any pointer type yield a total order, even if the built-in operators<
,>
,<=
,>=
do not.
So the solution for pointers would be:
template<typename T>
bool check(T *p, T *a, T *b)
{
return std::less_equal<T*>{}(a,p) && std::less<T*>{}(p,b);
}
Here's a working example using pointers.
There is no such strict guarantee for iterators; however this can be worked around in c++20, since it provides std::to_address which can convert pointable objects to pointers. Note, however, that the behavior of doing this for the purpose of comparisons is only really well defined for contiguous iterators.
Since we know that std::vector
iterators cover a contiguous range, we can use this to retrieve the underlying pointer (note: not dereference it, as this would be undefined behavior for the past-the-end pointer).
So for a std::vector<T>::iterator
, a solution might look like:
template <typename T>
bool check(const std::vector<T>::const_iterator p, std;:vector<T>::const_iterator a, std::vector<T>::const_iterator b)
{
// Delegate to the pointer check version defined above, for brevity
return check(std::to_address(p), std::to_address(a), std::to_address(b));
}
Here's a working example using iterators.
1 This same note exists all the way back to c++11 under §23.14.7/2, with similar wording.
回答4:
std::distance(first, last)
from C++17 can be used for both, but result is undefined if last
is unreachable from first
(e.g. different range or invalid iterator)
来源:https://stackoverflow.com/questions/64042325/in-c-how-to-check-a-pointer-lies-within-a-range