TL; DR: a+i
и &a[i]
являются правильно сформированными и дают нулевой указатель, когда a
является нулевым указателем и i
равно 0, в соответствии со (намерением) стандарта, ивсе составители согласны.
a+i
явно правильно сформировано в соответствии с [expr.add] / 4 последнего проекта стандарта:
Когда выражение J, имеющее целочисленный тип, добавляется или вычитается из выражения P типа указателя, результат имеет тип P.
- Если P оценивается как нулевое значение указателя, а J оценивается как0, результатом является нулевое значение указателя.
- [...]
&a[i]
сложно.Для [expr.sub] / 1 , a[i]
эквивалентно *(a+i)
, таким образом &a[i]
эквивалентно &*(a+i)
.Теперь в стандарте не совсем ясно, правильно ли сформирован &*(a+i)
, когда a+i
является нулевым указателем.Но, как указывает @nm в комментарии , цель, записанная в cwg 232 , состоит в том, чтобы разрешить этот случай.
Поскольку базовый язык UB требуется длябыть пойманным в константном выражении ( [expr.const] / (4.6) ), мы можем проверить, считают ли компиляторы эти два выражения UB.
Вот демонстрация, если компиляторы думаютконстантное выражение в static_assert
равно UB, или если они думают, что результат не true
, то они должны произвести диагностику (ошибку или предупреждение) по стандарту:
(обратите внимание, что это используетоднопараметрический static_assert и constexpr lambda, которые являются функциями C ++ 17, и лямбда-аргумент по умолчанию, который также является довольно новым)
static_assert(nullptr == [](char* a=nullptr, int i=0) {
return a+i;
}());
static_assert(nullptr == [](char* a=nullptr, int i=0) {
return &a[i];
}());
С https://godbolt.org/z/hhsV4I, кажется, что все компиляторы ведут себя одинаковов этом случае вообще не производит диагностики (что меня немного удивляет).
Однако это отличается от случая offset
.Реализация, опубликованная в , в этом вопросе явно создает ссылку (которая необходима для обхода пользовательского operator&
) и, таким образом, подчиняется требованиям к ссылкам.