Структура std :: memcpy с плотно упакованными членами типа TriviallyCopyable T в массив T и наоборот

Вот Установлено, что недопустимо рассматривать тесно упакованные последовательные члены структуры типа T как массив T.

Но как насчет копирования базового представления?

Дано:

struct vec {
float x, y, z;
};

с теми же ограничениями:

static_assert(sizeof(vec) == 3 * sizeof(float));

является следующим:

int main() {
vec v = {1.9f, 2.5f, 3.1f};

float a[3];
std::memcpy(&a, &v, 3 * sizeof(float));
assert(a[0] == v.x);
assert(a[1] == v.y);
assert(a[2] == v.z);

vec u;
std::memcpy(&u, &a, 3 * sizeof(float));
assert(u.x == a[0]);
assert(u.y == a[1]);
assert(u.z == a[2]);
}

законно?

0

Решение

Пока ваш тип структуры не имеет какого-либо дополнения, в стандарте нет явной поддержки для него, но можно предположить поддержку чего-то очень близкого к нему.

Дан тривиально копируемый тип Tявно разрешено копировать его представление в массив char (или же unsigned char) и назад.

Не требуется, чтобы содержимое массива сохранялось в самом массиве. Содержимое может быть сохранено в файле и перечитано при последующем выполнении программы. Или хранится в объекте другого типа, если этот тип позволяет это. Чтобы это работало, реализации должны позволять memcpyпредставление представлений в объекты, когда эти представления не происходят от объектов типа T в том же забеге.

В результате, по крайней мере,

int main() {
vec v = {1.9f, 2.5f, 3.1f};

float a[3];

assert(sizeof v == sizeof a);

{ char tmp[3 * sizeof(float)];
std::memcpy(tmp, &v, 3 * sizeof(float));
std::memcpy(a, tmp, 3 * sizeof(float)); }
assert(a[0] == v.x);
assert(a[1] == v.y);
assert(a[2] == v.z);

vec u;
{ char tmp[3 * sizeof(float)];
std::memcpy(tmp, a, 3 * sizeof(float));
std::memcpy(&u, tmp, 3 * sizeof(float)); }
assert(u.x == a[0]);
assert(u.y == a[1]);
assert(u.z == a[2]);
}

должен либо потерпеть неудачу на первом assertили пройти. Для любого представления, в котором оно потерпело бы неудачу, тривиально создать функцию, которая бы получала такое точное представление недвусмысленно действительными способами, поэтому она не должна давать сбой.

Теперь опуская tmp здесь немного ненадежно

std::memcpy это просто повторяющиеся присвоения отдельных байтов и могли быть прописаны в явном виде. Семантика = оператор подразумевает, что для тривиально копируемых типов, a = b; а также { auto tmp = b; a = tmp; } эквивалентны. То же самое с a = b; c = d; а также { auto tmp1 = b; auto tmp2 = d; a = tmp1; c = tmp2; }, и так далее. Первое, что прямой memcpy делает, это то, что два memcpyчерез tmp делать.

С другой стороны, разрешение на копирование в и из массива char можно прочитать как требующий фактического массива charа не просто его функциональный эквивалент.

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

1

Другие решения

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

https://en.wikipedia.org/wiki/Data_structure_alignment

Ваш код может нормально работать на компиляторе C ++ и не работать на другом или даже на том же компиляторе с другой конфигурацией.

Также обратите внимание, что вы используете указатель массива неправильно.

std::memcpy(a, &v, 3 * sizeof(float));

и не

std::memcpy(&a, &v, 3 * sizeof(float));

а — это уже постоянный указатель на число с плавающей точкой

-3

По вопросам рекламы [email protected]