Lección 15 · 15 de 20
optional, variant y visit · C++
Represente en C++17 valores que pueden faltar con std::optional y valores de varios tipos con std::variant y std::visit: puertos y celdas.

`std::optional<int>` es un int que puede no estar. findPort devuelve el puerto si el servicio aparece en la configuración y `std::nullopt` si no, sin recurrir a valores especiales como -1. El propio tipo de retorno avisa de que hay que comprobar el resultado.
Un optional vale true en una condición si tiene valor, y *port lo lee. `value_or(25)` da el valor o, si falta, el que se le pasa: el correo no está configurado y se usa el puerto 25.
`std::variant<int, double, std::string>` guarda exactamente uno de esos tipos cada vez, como una celda de una hoja de cálculo, que puede tener un entero, un decimal o un texto. using Cell = ... le da un nombre corto, y la fila row mezcla los tres tipos en un solo vector.
`std::visit` llama a la función adecuada según el tipo que guarde cada celda. CellPrinter es un visitante: un struct con un operator() para cada tipo. Si faltara alguno y no hubiera una conversión posible, el programa no compilaría, lo que evita olvidar casos.
Para preguntar por un tipo concreto está `std::holds_alternative<double>`, y `std::get<std::string>` saca el valor; si el tipo no coincide, lanza la excepción std::bad_variant_access.
Código · 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";}Salida · 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
Para recordar
std::optional<T>: unTo nada (std::nullopt).- Compruebe el
optionalconifantes de leerlo con*, o usevalue_or. std::variant<A, B, C>guarda uno de esos tipos cada vez.std::visitcon un visitante atiende todos los tipos del variant.std::get<T>lanza una excepción si el variant no guarda unT.
Ejercicio
Añada bool a los tipos de Cell y una celda true a la fila. Verá que sale como int 1, porque el bool se convierte en int para llamar al visitante. Añada a CellPrinter un operator()(bool) que escriba bool yes.