C++ Basics: Represent Missing Results with std::optional
A stock count of zero is a valid answer. An unknown product is different: the lookup has no answer. Return std::optional<int> to keep those cases separate.
Prepare a scratch directory
Tested on Linux x86_64 with GCC 14.2.0. Install G++ and its C++ standard-library development files, and make g++ available on PATH. You also need Bash, a text editor, and mktemp, rm, and rmdir. No third-party C++ library is required.
Keep the same terminal open. Stop if setup fails, before saving the source:
start_directory="$PWD"
work_directory="$(mktemp -d)" || exit 1
cd -- "$work_directory" || exit 1Run the example
Save this as optional-explicit-absence-demo.cpp:
#include <iostream>
#include <optional>
#include <string_view>
std::optional<int> lookup_stock(std::string_view product)
{
if (product == "pencil") {
return 0;
}
if (product == "notebook") {
return 12;
}
return std::nullopt;
}
int main()
{
for (const auto product : {"pencil", "notebook", "eraser"}) {
const auto stock = lookup_stock(product);
std::cout << product << ": ";
if (stock) {
std::cout << *stock << '\n';
} else {
std::cout << "unknown\n";
}
}
}Compile and run it. The second command runs only after compilation succeeds:
g++ -std=c++20 -Wall -Wextra -Werror -pedantic optional-explicit-absence-demo.cpp -o optional-explicit-absence-demo &&
./optional-explicit-absence-demoObserved output:
pencil: 0
notebook: 12
eraser: unknownRead the result
The pencil result contains zero, so its optional is engaged and the branch prints 0. The eraser result is disengaged, so the program never dereferences it. Checking an optional tests whether a value exists, not whether the contained integer is nonzero.
Apply it carefully
Dereference only after checking engagement. An optional represents absence, but does not explain why a lookup failed. Use a richer result type when callers need distinct failure reasons. value_or supplies a fallback value, which may intentionally erase the difference between missing data and a real value. This example needs only the C++17 optional interface, compiled in C++20 mode.
The C++ working draft specifies the relevant library behavior. The example uses interfaces available in C++20; the current working draft also describes later additions.
Clean up
Remove only the source and executable created here, then return to the starting directory:
rm -- optional-explicit-absence-demo.cpp optional-explicit-absence-demo
cd -- "$start_directory" || exit 1
rmdir -- "$work_directory"
unset start_directory work_directoryIf the directory is not empty, inspect the remaining contents before removing anything else.
Acknowledgements
This post was created with assistance from AI.