Подтвердить что ты не робот

Является ли бит std:: array совместимым со старым массивом C?

Является ли базовое представление бит для std::array<T,N> v и a T u[N] тем же?

Другими словами, безопасно ли скопировать N*sizeof(T) байты от одного к другому? (Либо через reinterpret_cast, либо memcpy.)

Edit:

Для пояснения акцент делается на том же представлении бит и reinterpret_cast.

Например, предположим, что у меня есть эти два класса над некоторым тривиально-скопируемым типом T для некоторого N:

struct VecNew {
    std::array<T,N> v;
};

struct VecOld {
    T v[N];
};

И есть устаревшая функция

T foo(const VecOld& x);

Если представления одинаковы, то этот вызов безопасен и позволяет избежать копирования:

VecNew x;
foo(reinterpret_cast<const VecOld&>(x));
4b9b3361

Ответ 1

Я говорю "да" (но стандарт этого не гарантирует).

В соответствии с [array]/2:

Массив - это совокупность ([dcl.init.aggr], которая может быть list-initialized с числом элементов до N, типы которых можно конвертировать в Т.

И [dcl.init.aggr]:

Агрегат - это массив или класс (раздел [класс]) с

  • нет пользовательских, явных или унаследованных конструкторов ([class.ctor]),

  • нет частных или защищенных нестатических данных (раздел [Class.access]),

  • нет виртуальных функций ([class.virtual]) и

  • нет виртуальных, частных или защищенных базовых классов ([class.mi]).

В свете этого "инициализируется списком" возможно только в том случае, если в начале класса нет других членов и нет vtable.

Затем data() указывается как:

constexpr T* data() noexcept;

Возвращает: указатель такой, что [data(), data() + size()) является допустимым диапазоном, а data() == addressof(front()).

Стандарт в основном хочет сказать "он возвращает массив", но оставляет дверь открытой для других реализаций.

Единственная возможная другая реализация - это структура с отдельными элементами, и в этом случае вы можете столкнуться с проблемами сглаживания. Но, на мой взгляд, этот подход не добавляет ничего, кроме сложности. Нет ничего, что можно получить, развернув массив в структуру.

Таким образом, не имеет смысла не использовать std::array как массив.

Но существует лазейка.

Ответ 2

Это напрямую не отвечает на ваш вопрос, но вы просто должны использовать std::copy:

T c[N];
std::array<T, N> cpp;

// from C to C++
std::copy(std::begin(c), std::end(c), std::begin(cpp));

// from C++ to C
std::copy(std::begin(cpp), std::end(cpp), std::begin(c));

Если T является тривиально-скопируемым типом, это скомпилируется до memcpy. Если это не так, то это будет выполнять поэтапное копирование и будет правильным. В любом случае, это делает правильную вещь и вполне читаемо. Нет необходимости в ручной байтовой арифметике.

Ответ 3

std::array предоставляет метод data(), который можно использовать для копирования в/из массива c-style соответствующего размера:

const size_t size = 123;
int carray[size];
std::array<int,size> array;

memcpy( carray, array.data(), sizeof(int) * size );
memcpy( array.data(), carray, sizeof(int) * size );

Как указано в документации

Этот контейнер представляет собой совокупный тип с той же семантикой, что и структура, содержащая массив C-стиля T [N] в качестве единственного нестатического элемента данных.

поэтому кажется, что размер памяти будет совместим с массивом c-style, хотя неясно, почему вы хотите использовать "хаки" с reinterpret_cast, когда есть правильный способ, который не имеет никаких накладных расходов.

Ответ 4

Требование к методу data() состоит в том, что он возвращает указатель T* такой, что:

[data(), data() + size()) - допустимый диапазон, а data() == addressof(front()).

Это означает, что вы можете получить доступ к каждому элементу последовательно с помощью указателя data(), и, если T тривиально можно копировать, вы действительно можете использовать memcpy для копирования sizeof(T) * size() байтов в/из массива T[size()], так как это эквивалентно memcpy каждому элементу индивидуально.

Однако вы не можете использовать reinterpret_cast, поскольку это будет нарушать строгий псевдоним, поскольку data() не требуется, чтобы на самом деле был подкреплен массивом, - а также, даже если вы должны были гарантировать, что std::array содержит массив, поскольку С++ 17 вы не можете (даже используя reinterpret_cast) направить указатель на массив в/из указателя на его первый член (вы должны использовать std::launder).

Ответ 5

array не имеет большого мандата о базовом типе, в котором вы его создаете.

Чтобы иметь возможность использовать полезные результаты при использовании memcpy или reinterpret_cast для копирования, тип, который вы им создали, должен быть тривиально скопирован. Сохранение этих элементов в array не влияет на требование, чтобы memcpy (и такое) работало только с тривиально-скопируемыми типами.

array требуется быть непрерывным контейнером и агрегатом, что в значительной степени означает, что хранилище для элементов должно быть массивом. Стандарт показывает это как:

T elems[N]; // exposition only

Впоследствии, однако, есть примечание, что, по крайней мере, подразумевается, что он является массивом (§ [array.overview]/4):

[Примечание: переменная-член elems показана только для изложения, чтобы подчеркнуть, что array - это совокупность классов. Имя elems не является частью интерфейса массивов. -end note]

[выделено курсивом]

Обратите внимание, что это действительно только конкретное имя elems, которое не требуется.