Someone asked me for help with their C++ code. The code was something like this:
std::wstring something;
std::optional<bool> result = something.empty() ?
ReadBoolValueFromRegistry() : {};
The programmer who wrote this code wanted to check whether the ‘something’ string was empty, and if it was, a boolean value had to be read from the Windows registry, and then stored into the std::optional result variable.
On the other hand, if the ‘something’ string was not empty, the std::optional result should be default-initialized to an empty optional (i.e. an optional that doesn’t contain any value).
That was the programmer’s intention. Unfortunately, their code failed to compile with Visual Studio 2019 (C++17 mode was enabled).
There were squiggles under the opening brace {, and the Visual C++ compiler emitted the following error messages:
Error Code | Description |
C2059 | syntax error: ‘{‘ |
C2143 | syntax error: missing ‘;’ before ‘{‘ |
I was asked: “What’s the problem here? Are there limitations of using the {} syntax to specify nothing?”
This is a good question. So, clearly, the C++ compiler didn’t interpret the {} syntax as a way to default-initialize the std::optional in case the string was not empty (i.e. the second “branch” in the conditional ternary operator).
A first step to help the C++ compiler figuring out the programmer’s intention could be to be more explicit. So, instead of using {}, you can try and use the std::nullopt constant, which represents an optional that doesn’t store any value.
// Attempt to fix the code: replace {} with std::nullopt
std::optional<bool> result = something.empty() ?
ReadBoolValueFromRegistry() : std::nullopt;
Unfortunately, this code doesn’t compile either.
Why is that?
Well, to figure that out, let’s take a look at the C++ conditional operator (?:). Consider the conditional operator in its generic form:
// C++ conditional operator ?:
exp1 ? exp2 : exp3
In the above code snippet, “exp2” is represented by the ReadBoolValueFromRegistry call. This function returns a bool. So, in this case the return type of the conditional operator is bool.
// Attempt to fix the code: replace {} with std::nullopt
std::optional<bool> result = something.empty() ?
ReadBoolValueFromRegistry() : std::nullopt;
// ^^^--- exp2 ^^^--- exp3
// Type: bool
On the other hand, if you look at “exp3”, you see std::nullopt, which is a constant of type std::nullopt_t, not a simple bool value!
So, you have this kind of type mismatch, and the C++ compiler complains. This time, the error message is:
Error C2446 ‘:’: no conversion from ‘const std::nullopt_t’ to ‘bool’
So, to fix that code, I suggested to “massage” it a little bit, like this:
// Rewrite the following code:
//
// std::optional<bool> result = something.empty() ?
// ReadBoolValueFromRegistry() : {};
//
// in a slightly different manner, like this:
//
std::optional<bool> result{};
if (something.empty()) {
result = ReadBoolValueFromRegistry();
}
Basically, you start with a default-initialized std::optional, which doesn’t contain any value. And then you assign the bool value read from the registry only if the particular condition is met.
The above C++ code compiles successfully, and does what was initially in the mind of the programmer.
P.S. I’m not a C++ “language lawyer”, but it would be great if the C++ language could be extended to allow the original simple code to just work:
std::optional<bool> result = something.empty() ?
ReadBoolValueFromRegistry() : {};