48 Intermediate The Hundred
The optional That Is Always True
the setting the user switched off, and the program never got the memo
#include <iostream>
#include <optional>
#include <string>
// Reads a switch from the config file; nullopt means the user never set it.
std::optional<bool> setting(const std::string& name) {
if (name == "dark_mode")
return false; // the user switched this one off
if (name == "telemetry")
return true;
return std::nullopt;
}
int main() {
for (std::string name : {"dark_mode", "telemetry", "autosave"}) {
if (setting(name))
std::cout << name << ": on\n";
else
std::cout << name << ": off\n";
}
}
Run it. Which settings come out on?
Answer
dark_mode is false — and the program switches it on anyway.
Why¶
if (opt) asks std::optional exactly one question: are you holding a value? Its
explicit operator bool is has_value() under another spelling, and it never looks
inside the box. An optional<bool> holding false is holding a value, so it is
truthy; only the empty autosave reaches the else, where "off" is right for the wrong
reason. The same shape bites std::optional<int> holding 0. Nothing here is ill-formed
or undefined, and GCC 11 with -Wall -Wextra compiles it silently — emptiness and falsity
are two different "no"s, and if only ever hears the first one.
The fix¶
Ask about the value, not about the box:
if (setting(name).value_or(false)) // "not configured" counts as off
auto s = setting(name); // or keep the two cases apart:
if (s && *s) // has a value, and that value is true
Takeaway: for optional, "has a value" and "is true" are different questions — if
only ever asks the first.
Try it: g++ -std=c++17 main.cpp -o demo && ./demo — std::optional needs C++17 or newer.