Can ptrdiff_t represent all subtractions of pointers to elements of the same array object?
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.