Могу ли я безопасно получить указатель на неинициализированное содержимое std :: optional для чтения в память из двоичного файла без создания содержимого по умолчанию?

Я занимаюсь рефакторингом устаревшего кода, который считывает некоторые двоичные данные из файла в структуру. Мне пришло в голову, что изменение переменной на std :: optional может обеспечить фактическое чтение (инициализацию) переменной перед ее использованием. Но для кода чтения файла нужен адрес переменной. Есть ли способ (предполагаемый способ, а не взлом) сказать std :: optional «дайте мне указатель на ваш неинициализированный T, чтобы я мог записать содержимое этой памяти» и «продолжайте и изменить свое состояние с пустого на полноценное "?

(То есть вместо назначения T по умолчанию (что, если T не имеет конструктора по умолчанию), а затем взятия адреса созданного значения по умолчанию.)

Упрощенный пример:

struct FILE_HEADER { /* some data members... */ };

class FileFrobulator
{
private:
   FILE_HEADER fileHead;

public:
   void called_first(IFileReader* reader)
   {
       // ...
       reader->read(&fileHead, sizeof(FILE_HEADER));
       // ...
   }

   void called_later(IFileReader* reader)
   {
      // ...
      // use fileHead.foo, fileHead.bar, etc. while reading rest of file
      // ...
   }
};

Вопрос в том, если я изменю член на std::optional<FILE_HEADER> fileHead;, что мне тогда изменить для строки, которая в настоящее время читает reader->read(&fileHead, sizeof(FILE_HEADER));?

Я мог сделать это:

fileHead = FILE_HEADER();
reader->read(&*fileHead, sizeof(FILE_HEADER));

Вы, возможно, возражали ранее, что функция, которая принимает адрес неинициализированного T в std :: optional и устанавливает необязательное значение, которое больше не является пустым, рискует случайно оставить необязательный помеченный значимым, но все еще неинициализированный, если пользователь не т собственно напиши в память. Однако обратите внимание, что в приведенном выше примере кода возникает аналогичный, хотя и меньший риск: если выдает команду reader-> read (), необязательный параметр больше не будет пустым, но он также недействителен для использования. Созданный по умолчанию T, возможно, лучше, чем неинициализированный T, однако, если FILE_HEADER является структурой C (что в данном случае так), ее члены все еще не инициализированы!

Может так лучше:

FILE_HEADER temp;
reader->read(&temp, sizeof(FILE_HEADER));
fileHead = temp;

Но оба они включают избыточную инициализацию и / или копирование (возможно, оптимизированное компилятором).


person Dennis    schedule 19.03.2020    source источник
comment
Вы должны создать экземпляр FILE_HEADER в вашем std::optional. Может быть, используя метод emplace(). Пока вы этого не сделаете, он не существует, и попытка нацарапать что-то там, где, по вашему мнению, должно быть, в лучшем случае ничего не даст, а в худшем - приведет к сбою.   -  person Sam Varshavchik    schedule 19.03.2020


Ответы (1)


Есть ли способ (предполагаемый способ, а не взлом), чтобы сообщить std :: optional «дайте мне указатель на ваш неинициализированный T, чтобы я мог записать содержимое этой памяти» и «продолжайте и изменяйте свое состояние с пустого на значение -полно сейчас "?

Обе эти вещи были бы ложью.

Пустой optional<T> не имеет T, инициализирован он или нет, поэтому вы получите указатель / ссылку на несуществующий объект. И состояние optional<T> должно отражать, существует ли T в optional<T>. Поскольку копирование битов само по себе не вызывает появления T, оно не будет существовать, если пользователь напрямую не создаст T. Так что притворяться, что T существует, когда его нет, тоже ложно.

Чтобы сделать то, что вы намереваетесь, у вас просто должна быть функция, которая возвращает optional<FILE_HEADER>:

fileHead = readFileHeader(reader);

Если функция чтения выдает ошибку, значение в FileFrobulator никогда не устанавливается. Вы даже можете сделать это лямбда-функцией, если хотите:

fileHead = [](IFileReader* reader) {
  std::optional<FILE_HEADER> ret(std::in_place);
  reader->read(&*ret, sizeof(FILE_HEADER));
  return ret;
}(reader);
person Nicol Bolas    schedule 19.03.2020
comment
Наверное &*ret ты имел ввиду? - person Red.Wave; 19.03.2020
comment
Пустой необязательный ‹T› не имеет T, инициализирован он или нет - в нем есть место для T, не так ли? - person user253751; 19.03.2020
comment
@ Red.Wave: Нет; он должен возвращать optional; в случае исключения fileHead останется пустым. Кроме того, возврат ссылки на уничтоженный объект (как это делает &*ret) бесполезен. - person Nicol Bolas; 19.03.2020
comment
@ user253751: там есть место для буквы T, не так ли? Это не значит, что у него есть один. - person Nicol Bolas; 19.03.2020
comment
@NicolBolas Ну, спрашивающий хочет построить букву T в этом пространстве. - person user253751; 19.03.2020
comment
@ user253751: OP ни разу не говорит, что он хочет построить T. Если бы он это сделал, он бы просто использовал для этого существующий API: optional::emplace. Он специально хочет обойти конструкцию, обращаясь к памяти напрямую без T существующего сначала, копируя некоторые биты, а затем обманывая optional, заставляя думать, что память теперь содержит T. - person Nicol Bolas; 19.03.2020
comment
Это поднимает интересный момент. Но есть ли там место? Я имею в виду, что там, вероятно, есть место, но нужно ли это? (Т.е. является реализацией или специализацией необязательного, который куча выделяет по запросу в соответствии со стандартом?) Тогда у вас есть случай, когда специализация заимствует неиспользуемые биты для обозначения пустого состояния (что, как я считаю, разрешено, такое что касается указателей). Есть ли у него место, если он переписывает биты? - person Dennis; 19.03.2020
comment
Я ценю, что ваш ответ подсказал мне std :: in_place. - person Dennis; 19.03.2020
comment
Пожалуйста, устраните неоднозначность синтаксической ошибки в конце вашего сообщения. Что вы имели в виду под &*filehead? - person Red.Wave; 19.03.2020
comment
Я думаю, он имел в виду &*ret внутри лямбды. - person Dennis; 19.03.2020
comment
@ Деннис: Я имею в виду, что там, вероятно, есть место, но нужно ли это? Да. - person Nicol Bolas; 19.03.2020