Определено ли преобразование между векторами поведения? - PullRequest
0 голосов
/ 21 января 2019

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

Я хочу получить указатель data ()из вектора, если у меня есть только пустота *, указывающая на вектор.Я пытаюсь преобразовать из std::vector<T> в std::vector<char>, чтобы получить указатель data ().Я хочу знать, определен ли следующий код поведения и не будет ли он действовать по-разному в разных ситуациях.

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> ints = { 0, 1, 2, 3, 4 };

    std::vector<char>* memory = reinterpret_cast<std::vector<char>*>(&ints);
    int *intArray = reinterpret_cast<int *>(memory->data());

    std::cout << intArray[0] << intArray[1] << intArray[2] << intArray[3] << intArray[4] << std::endl; //01234 Works on gcc and vc++
    std::getchar();
}

Кажется, это работает в этом изолированном случае, но я не знаю, даст ли этоошибки или неопределенное поведение внутри кода сериализации.

Ответы [ 2 ]

0 голосов
/ 21 января 2019

Я не думаю, что это UB.

С reinterpret_cast<std::vector<char>*>(&ints) вы приводите векторный объект к другому векторному объекту другого (и фактически несовместимого) типа. Тем не менее, вы не разыменовываете результирующий указатель, и - поскольку оба векторных объекта, скорее всего, будут иметь одинаковые ограничения псевдонимов, приведение будет в порядке. Cf, например, это онлайн-проект C ++). Обратите внимание, что вектор не хранит типы данных «на месте», но будет содержать указатель на значения.

5.2.10 Повторное толкование акта

(7) Указатель объекта может быть явно преобразован в указатель объекта другой тип. 70 Когда значение v типа «указатель на T1» равно преобразованный в тип «указатель на cv T2», результат будет static_cast (static_cast (v)), если оба T1 и T2 имеют стандартную компоновку типы ([basic.types]) и требования выравнивания T2 более строгие, чем у T1, или если любой из типов является недействительным. Преобразование prvalue типа «указатель на T1» на тип «указатель на T2» (где T1 и T2 являются типами объектов и где требования выравнивания T2 не более строгие, чем у T1), и обратно в исходный тип дает исходное значение указателя. Результат любого другого такого указателя конверсия не указана.

Таким образом, приведение векторного объекта вперед и назад должно работать определенным образом.

Во-вторых, вы применяете указатель, который изначально указывает (и имеет псевдоним) int «назад» к исходному типу int. Так что псевдонимы явно не нарушаются.

Я не вижу здесь никакого UB (если только у векторного объекта не было более строгих правил наложения имен, чем у векторного объекта, что, скорее всего, не так).

0 голосов
/ 21 января 2019

Это нарушение псевдонимов:

std::vector<char>* memory = reinterpret_cast<std::vector<char>*>(&ints);
int *intArray = reinterpret_cast<int *>(memory->data());

Для [basic.life] доступ к memory->data() здесь имеет неопределенное поведение.

Способ обойти это - вызвать ints.data(), чтобы получить указатель int* на основной непрерывный массив. После этого вам разрешено привести его к void*, char* или unsigned char* (или std::byte* в C ++ 17).

Оттуда вы можете вернуться к int*, чтобы снова получить доступ к элементам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...