Для вычитания указателей i
а также j
к элементам одного и того же объекта массива примечание в [expr.add # 5] гласит:
[ Замечания: Если значение I-J не находится в диапазоне представимых значений типа std::ptrdiff_t
, поведение не определено. - конечная нота ]
Но учитывая [Support.types.layout # 2], в котором говорится, что (акцент мой):
- Тип
ptrdiff_t
является определяемым реализацией целочисленным типом со знаком, который может держать разница двух индексов в объекте массива, как описано в [Expr.add].
Возможно ли это даже в результате i-j
не быть в диапазоне представимых значений ptrdiff_t
?
PS: Я прошу прощения, если мой вопрос вызван моим плохим пониманием английского языка.
РЕДАКТИРОВАТЬ: Связанные с: Почему максимальный размер массива "слишком большой"?
Возможно ли это даже в результате
i-j
не быть в диапазоне представимых значенийptrdiff_t
?
Да, но вряд ли.
По факту, [support.types.layout]/2
не говорит много, кроме правильных правил вычитания указателей и ptrdiff_t
определены в [expr.add]
, Итак, давайте посмотрим на этот раздел.
[expr.add]/5
Когда вычитаются два указателя на элементы одного и того же объекта массива, тип результата является определяемым реализацией знаковым целочисленным типом; этот тип должен быть того же типа, который определен как
std::ptrdiff_t
в<cstddef>
заголовок.
Прежде всего, обратите внимание, что случай, когда i
а также j
Индексы индексов разных массивов не рассматриваются. Это позволяет лечить i-j
как P-Q
будет где P
указатель на элемент массива в нижнем индексе i
а также Q
указатель на элемент тот же самый массив в нижнем индексе j
, На самом деле вычитание двух указателей на элементы разных массивов неопределенное поведение:
[expr.add]/5
Если выражения
P
а такжеQ
указать соответственно на элементыx[i]
а такжеx[j]
одного и того же объекта массиваx
, выражениеP - Q
имеет значениеi−j
; в противном случае поведение не определено.
В заключение, с обозначениями, определенными ранее, i-j
а также P-Q
определены как имеющие одинаковое значение, причем последний имеет тип 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
переполниться. Но нет никаких гарантий на это.
Я думаю, что это ошибка формулировок.
Правило в [expr.add] унаследовано от того же правила для вычитания указателя в стандарте C. В стандарте C ptrdiff_t
не требуется хранить разницу двух индексов в объекте массива.
Правило в [support.types.layout] исходит от Базовая языковая проблема 1122. Добавлены прямые определения std::size_t
а также std::ptrdiff_t
, которая должна решить проблему кругового определения. Я не вижу никаких причин (по крайней мере, не упомянутых ни в одном официальном документе) сделать std::ptrdiff_t
держать любую разницу двух индексов в объекте массива. Я думаю, что он просто использует неправильное определение для решения проблемы кругового определения.
В качестве еще одного доказательства, [Diff.library] не упоминает никакой разницы между std::ptrdiff_t
в C ++ и ptrdiff_t
в к. с в к ptrdiff_t
не имеет такого ограничения, в C ++ std::ptrdiff_t
не должно быть такого ограничения тоже.