Skip to content

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: on
telemetry: on
autosave: off

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 && ./demostd::optional needs C++17 or newer.

Open in Compiler Explorer ↗ Quiz this entry