I'm refactoring some legacy code which reads some binary data from a file into struct. It occurred to me that changing the variable to a std::optional could ensure the variable actually read (initialized) before it is used. But the file reading code needs the address of the variable. Is there a way (an intended way not a hack) to tell a std::optional "give me a pointer to your uninitialized T so I can write the contents of that memory" and "go ahead and change your state from empty to value-ful now"?
(That is, instead of assigning a default T (what if T has no default constructor) and then taking the address of the default constructed value.)
Simplified example:
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
// ...
}
};
The question is if I change the member to std::optional<FILE_HEADER> fileHead;
what then do I change for the line that currently reads reader->read(&fileHead, sizeof(FILE_HEADER));
?
I could do this:
fileHead = FILE_HEADER();
reader->read(&*fileHead, sizeof(FILE_HEADER));
You may have objected earlier that a feature to take the address of an uninitialized T in a std::optional and set the optional as no longer empty runs the risk of accidentally leaving the optional marked value-ful while still uninitialized if the user doesn't in fact write the memory. However note that the code example above runs a similar albeit lesser risk: if reader->read() throws, the optional will no longer be empty, but it also isn't valid for use. Default constructed T is arguably better than uninitialized T, however if FILE_HEADER is a C struct (which in this case it is) it's members are still uninitialized!
Maybe this is better:
FILE_HEADER temp;
reader->read(&temp, sizeof(FILE_HEADER));
fileHead = temp;
But both of those involve a redundant initialization and/or copy (perhaps optimized-out by the compiler).