Доступ к элементам массива массивов с использованием общего итератора

Это поведение undefined в С++ для доступа к элементам в смежных массивах, как в следующем коде?

#include <type_traits>
#include <algorithm>
#include <iterator>

int main()
{
    int a[10][10];
    static_assert(std::is_standard_layout< decltype(a) >::value, "!");
    std::fill(std::begin(*std::begin(a)), std::end(*std::prev(std::end(a))), 0);
    struct B { int b[10]; };
    B b[10];
    static_assert(std::is_standard_layout< decltype(b) >::value, "!");
    std::fill(std::begin(std::begin(b)->b), std::end(std::prev(std::end(b))->b), 0);
}

Технически я думаю, что для POD-типов законным является доступ к основной памяти любым способом, но как насчет std::* материала?

Что делать, если я изменяю все begin/end на rbegin/rend?

Ответ 1

Да, это UB.

Из [basic.compound]:

Каждое значение типа указателя является одним из следующих:

  • указатель на объект или функцию (указывается, что указатель указывает на объект или функцию) или
  • указатель за концом объекта ([expr.add]) или
  • значение нулевого указателя ([conv.ptr]) для этого типа или
  • неверное значение указателя.

Значение типа указателя, которое является указателем на конец объекта или мимо него, представляет адрес первого байта в памяти ([intro.memory]), занятый объектом или первым байтом в памяти после окончания занимаемого объектом, соответственно. [Примечание. Указатель за конец объекта ([expr.add]) не считается указывающим на несвязанный объект типа объекта, который может быть расположен по этому адресу. Значение указателя становится недействительным, когда хранящееся хранилище заканчивается его длительностью хранения; см. [basic.stc]. - конечная нота]

И [expr.add]/4:

Когда выражение, которое имеет интегральный тип, добавляется или вычитается из указателя, результат имеет тип операнда указателя. Если выражение P указывает на элемент x [i] объекта массива x с n элементами, 86 выражения P + J и J + P (где J имеет значение j) указывают на (возможно, гипотетический) элемент x [i + j], если 0≤i + j≤n; в противном случае поведение undefined. Аналогично, выражение P - J указывает на (возможно, гипотетический) элемент x [i-j], если 0≤i-j≤n; в противном случае поведение undefined.

Итак, &a[0][0] + 10 - это "указатель мимо конца объекта", это минус конечный указатель первого массива. Вы не можете добавить еще один указатель - для этого случая нет определенного поведения.

Указатель не может быть как указателем "за конец", так и "указателем на объект" (интерпретация &a[0][0] + 10, как если бы он был &a[1][0]). Это одно или другое.