For subtraction of pointers i
and j
to elements of the same array object the note in [expr.add#5] reads:
[ Note: If the value i−j is not in the range of representable values of type
std::ptrdiff_t
, the behavior is undefined. — end note ]
But given [support.types.layout#2], which states that (emphasis mine):
- The type
ptrdiff_t
is an implementation-defined signed integer type that can hold the difference of two subscripts in an array object, as described in [expr.add].
Is it even possible for the result of i-j
not to be in the range of representable values of ptrdiff_t
?
PS: I apologize if my question is caused by my poor understanding of the English language.
EDIT: Related: Why is the maximum size of an array "too large"?
Is it even possible for the result of
i-j
not to be in the range of representable values ofptrdiff_t
?
Yes, but it's unlikely.
In fact, [support.types.layout]/2
does not say much except the proper rules about pointers subtraction and ptrdiff_t
are defined in [expr.add]
. So let us see this section.
[expr.add]/5
When two pointers to elements of the same array object are subtracted, the type of the result is an implementation-defined signed integral type; this type shall be the same type that is defined as
std::ptrdiff_t
in the<cstddef>
header.
First of all, note that the case where i
and j
are subscript indexes of different arrays is not considered. This allows to treat i-j
as P-Q
would be where P
is a pointer to the element of an array at subscript i
and Q
is a pointer to the element of the same array at subscript j
. In deed, subtracting two pointers to elements of different arrays is undefined behavior:
[expr.add]/5
If the expressions
P
andQ
point to, respectively, elementsx[i]
andx[j]
of the same array objectx
, the expressionP - Q
has the valuei−j
; otherwise, the behavior is undefined.
As a conclusion, with the notation defined previously, i-j
and P-Q
are defined to have the same value, with the latter being of type std::ptrdiff_t
. But nothing is said about the possibility for this type to hold such a value. This question can, however, be answered with the help of std::numeric_limits
; especially, one can detect if an array some_array
is too big for std::ptrdiff_t
to hold all index differences:
static_assert(std::numeric_limits<std::ptrdiff_t>::max() > sizeof(some_array)/sizeof(some_array[0]),
"some_array is too big, subtracting its first and one-past-the-end element indexes "
"or pointers would lead to undefined behavior as per [expr.add]/5."
);
Now, on usual target, this would usually not happen as sizeof(std::ptrdiff_t) == sizeof(void*)
; which means an array would need to be stupidly big for ptrdiff_t
to overflow. But there is no guarantee of it.
I think it is a bug of the wordings.
The rule in [expr.add] is inherited from the same rule for pointer subtraction in the C standard. In the C standard, ptrdiff_t
is not required to hold any difference of two subscripts in an array object.
The rule in [support.types.layout] comes from Core Language Issue 1122. It added direct definitions for std::size_t
and std::ptrdiff_t
, which is supposed to solve the problem of circular definition. I don't see there is any reason (at least not mentioned in any official document) to make std::ptrdiff_t
hold any difference of two subscripts in an array object. I guess it just uses an improper definition to solve the circular definition issue.
As another evidence, [diff.library] does not mention any difference between std::ptrdiff_t
in C++ and ptrdiff_t
in C. Since in C ptrdiff_t
has no such constraint, in C++ std::ptrdiff_t
should not have such constraint too.
来源:https://stackoverflow.com/questions/49380475/can-ptrdiff-t-represent-all-subtractions-of-pointers-to-elements-of-the-same-arr