Lesson 15 · 15 of 20
optional, variant and visit · C++
Model values that may be missing with std::optional, and values of several types with std::variant and std::visit, in C++17: ports and cells.

`std::optional<int>` is an int that may not be there. findPort returns the port if the service is in the configuration and `std::nullopt` otherwise, with no special values such as -1. The return type itself tells callers they have to check the result.
An optional is true in a condition when it holds a value, and *port reads it. `value_or(25)` gives the value or, if it is missing, the one you pass: mail is not configured, so port 25 is used.
`std::variant<int, double, std::string>` holds exactly one of those types at a time, like a spreadsheet cell that may contain a whole number, a decimal or some text. using Cell = ... gives it a short name, and row mixes all three types in a single vector.
`std::visit` calls the right function for whatever type each cell holds. CellPrinter is a visitor: a struct with one operator() per type. If one were missing and no conversion fitted, the program would not compile, so no case can be forgotten.
To ask about one particular type there is `std::holds_alternative<double>`, and `std::get<std::string>` takes the value out; if the type does not match, it throws std::bad_variant_access.
Code · main.cpp
#include <iostream>#include <map>#include <optional>#include <string>#include <variant>#include <vector>// optional: a value that may be missing, with no magic numbers such as -1.std::optional<int> findPort(const std::map<std::string, int>& config, const std::string& service) { auto it = config.find(service); if (it == config.end()) return std::nullopt; return it->second;}using Cell = std::variant<int, double, std::string>; // a spreadsheet cell: one of thesestruct CellPrinter { // a visitor: one operator() per type void operator()(int n) const { std::cout << "int " << n << "\n"; } void operator()(double d) const { std::cout << "double " << d << "\n"; } void operator()(const std::string& s) const { std::cout << "text \"" << s << "\"\n"; }};int main() { std::map<std::string, int> config = {{"http", 8080}, {"db", 5432}}; for (std::string service : {"db", "mail"}) { std::optional<int> port = findPort(config, service); if (port) std::cout << service << " port: " << *port << "\n"; else std::cout << service << " port: not set, default " << port.value_or(25) << "\n"; } std::vector<Cell> row = {42, 3.75, std::string("Total"), 7}; for (const Cell& c : row) std::visit(CellPrinter{}, c); std::cout << "Cell 1 is a double? " << (std::holds_alternative<double>(row[1]) ? "yes" : "no") << ", cell 2 says " << std::get<std::string>(row[2]) << "\n";}Output · c++ -std=c++17 main.cpp -o main && ./main
db port: 5432 mail port: not set, default 25 int 42 double 3.75 text "Total" int 7 Cell 1 is a double? yes, cell 2 says Total
Key points
std::optional<T>: aTor nothing (std::nullopt).- Check an
optionalwithifbefore reading it with*, or usevalue_or. std::variant<A, B, C>holds one of those types at a time.std::visitwith a visitor handles every type in the variant.std::get<T>throws if the variant does not hold aT.
Exercise
Add bool to the types of Cell and a true cell to the row. It prints as int 1, because the bool is converted to int to call the visitor. Add an operator()(bool) to CellPrinter that prints bool yes.