Может ли ptrdiff_t представлять все вычитания указателей на элементы одного и того же объекта массива?

Для вычитания указателей i и j элементам одного и того же объекта массива примечание в [expr.add # 5] гласит:

[Примечание. Если значение i-j не находится в диапазоне представляемых значений типа std​::​ptrdiff_t, поведение не определено. - конечная нота]

Но учитывая [support.types.layout # 2], в котором говорится, что (внимание мое):

  1. Тип ptrdiff_t представляет собой целочисленный тип ptrdiff_t, определенный реализацией, который может содержать разность двух индексов в объекте массива, как описано в [expr.add].

Возможно ли даже, что результат ij не должен находиться в диапазоне представимых значений ptrdiff_t?

PS: Прошу прощения, если мой вопрос вызван моим плохим пониманием английского языка.

EDIT: Related: Почему максимальный размер массива "слишком велик"?

Ответы

Ответ 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 для переполнения. Но нет никакой гарантии.

Ответ 2

Я думаю, что это ошибка в формулировках.

Правило в [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 в C. Поскольку в C ptrdiff_t не существует такого ограничения, в C++ std::ptrdiff_t не должно иметь такого ограничения тоже.