Гарантии, если таковые имеются, сохранения представления значений при низкоуровневом создании/уничтожении объектовC++

Программы на C++. Форум разработчиков
Ответить
Anonymous
 Гарантии, если таковые имеются, сохранения представления значений при низкоуровневом создании/уничтожении объектов

Сообщение Anonymous »

На SO регулярно задают вопросы о допустимости операций по каламбуру. Например, я участвовал в недавнем: Является ли reinterpret_cast от char* до uint32_t* неопределенным поведением в CPP?
Но у меня есть вопрос, который пришел мне в голову при ответе на вышеизложенное: можно проиллюстрировать этим фрагментом:

Код: Выделить всё

#include 
#include 
#include 
#include 

struct S {
alignas(std::uint64_t) alignas(std::uint32_t)
std::array storage = {some init values};
};

int main()
{
S s;
std::uint64_t* pi64 = std::start_lifetime_as(s.storage.data());
// implicitely creating an uint32_t at the same location, ending lifetime of the uint64_t there
std::uint32_t* pi32 = std::launder(reinterpret_cast(s.storage.data()));
// is there any guarantee that the value representation is unchanged?
pi64 = std::start_lifetime_as(s.storage);
// is s.storage guaranteed to be unchanged?
}
Пока это теоретический код, поскольку мне не известна поддержка компилятора для std::start_lifetime_as. Но я считаю полезным проиллюстрировать мое понимание проблемы.
При определении pi64 я явно запускаю объект std::uint64_t, представление значения которого находится в уровень байта, тот, который я сохранил в массиве байтов.
Но затем я неявно создаю перекрывающийся std::uint32_t, который, AFAIU, завершает срок службы упомянутого std::uint64_t.
Что происходит с базовыми байтами? Каковы стандартные правила (я знаю, что беззнаковый символ/

Код: Выделить всё

std::byteУ массивов 
есть особые правила)?

Я оставляю исходный вопрос выше, но относительно https://stackoverflow.com/a/61442225 /21691539, этот фрагмент не имеет смысла, поскольку std::uint32_t* pi32 = std::launder(reinterpret_cast(s.storage.data())); это не так, вопреки моему утверждению выше — повторное использование хранилища.
Осмысленный фрагмент будет выглядеть так:

Код: Выделить всё

#include 
#include 
#include 
#include 

struct S {
alignas(std::uint64_t) alignas(
std::uint32_t) std::array storage = {
some init values};
};

int main() {
S s;
// explicitly creating an uint64_t at the storage location
std::uint64_t* pi64 =
std::start_lifetime_as(s.storage.data());
// explicitly reusing storage, thus ending the uint64_t lifetime
new (storage.data()) std::byte[sizeof(std::uint64_t)];
// implicitely creating an uint32_t at the same location, ending lifetime of
// the uint64_t there
std::uint32_t* pi32 =
std::launder(reinterpret_cast(s.storage.data()));
// explicitly re-creating an uint64_t at the storage location
// is it sufficient to end the uint32_t lifetime or do I need an explicit
// storage reuse as above?
pi64 = std::start_lifetime_as(s.storage);
// is s.storage guaranteed to be unchanged?
}
Таким образом, вопрос остается прежним, если сказать по-другому: гарантированно ли *pi64 будет одинаковым до и после повторного использования хранилища?
Обратите внимание, что этот фрагмент все еще может быть неправильным, см. комментарий перед вторым std::start_lifetime_as. Кто-то может сказать мне в комментарии, нужна ли мне дополнительная строка для завершения срока службы std::uint32_t, и я соответствующим образом исправлю фрагмент.

Подробнее здесь: https://stackoverflow.com/questions/791 ... -low-level
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C++»