Ответ 1
Возможно ли даже, что результат
ij
не должен находиться в диапазоне представимых значенийptrdiff_t
?
Да, но это маловероятно.
Фактически, [support.types.layout]/2
не говорит много, кроме правильных правил вычитания указателей, а ptrdiff_t
определены в [expr.add]
. Итак, посмотрим на этот раздел.
[expr.add]/5
Когда два указателя на элементы одного и того же объекта массива вычитаются, тип результата представляет собой определенный интегральным типом, определенный реализацией; этот тип должен быть того же типа, который определяется как
std::ptrdiff_t
в заголовке<cstddef>
.
Прежде всего, обратите внимание, что случай, когда i
и j
- индексы индексов разных массивов, не рассматривается. Это позволяет рассматривать ij
как PQ
, где P
является указателем на элемент массива в индексе i
а Q
является указателем на элемент того же массива в индексе j
. В деле вычитание двух указателей на элементы разных массивов - это неопределенное поведение:
[expr.add]/5
Если выражения
P
иQ
указывают соответственно на элементыx[i]
иx[j]
одного и того же объекта массиваx
, выражениеP - Q
имеет значениеi−j
; в противном случае поведение не определено.
В качестве вывода, с обозначением, определенным ранее, ij
и PQ
определены равными значениям, причем последний имеет тип std::ptrdiff_t
. Но ничего не говорится о возможности того, чтобы этот тип имел такую ценность. Однако на этот вопрос можно ответить с помощью std::numeric_limits
; особенно, можно определить, является ли массив some_array
слишком большим для std::ptrdiff_t
чтобы удерживать все разности индексов:
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."
);
Теперь, на обычной цели, это обычно не происходит как sizeof(std::ptrdiff_t) == sizeof(void*)
; что означает, что массив должен быть глупо большим для ptrdiff_t
для переполнения. Но нет никакой гарантии.