Словарные типы: optional, variant и tuple
Типобезопасные средства для "возможно есть значение", "один из нескольких типов" и группировки
union.
- использовать
std::optional<T>для значения, которое может присутствовать или отсутствовать, - использовать
std::variant<A, B, ...>как типобезопасное объединение, всегда знающее, что в нём хранится, - группировать несколько значений с помощью
std::pairиstd::tuple, - аккуратно распаковывать любой из них с помощью структурных привязок.
Эта тема предполагает, что вы уверенно владеете шаблонами, ссылками и семантикой перемещения из предыдущих разделов курса. Все примеры компилируются компилятором C++17.
1. std::optional - возможно, значение
Функции часто нужно сказать "вот результат" или "результата нет". Старые способы выразить это ненадёжны: вернуть магическую заглушку вроде -1 (а если -1 - допустимый ответ?), вернуть bool и записать настоящий ответ через выходной параметр или вернуть сырой указатель, который может быть nullptr (кто им владеет?). std::optional<T> (из <optional>) делает "отсутствие" полноценным, проверяемым типом состоянием.
optional<T> либо содержит T, либо пуст. Вы проверяете его как bool, читаете значение через * или -> (только когда оно есть) и задаёте запасной вариант через value_or. Пустое состояние записывается как std::nullopt.
#include <optional>
#include <string>
using namespace std;
optional<int> parsePort(const string& scheme) {
if (scheme == "http") return 80;
if (scheme == "https") return 443;
return nullopt; // нет разумного значения
}
Вызывающий не может забыть проверить, потому что значение находится внутри optional и его сначала нужно открыть:
auto p = parsePort("https");
if (p) { // есть значение?
cout << "port = " << *p << "\n"; // безопасно: мы проверили
}
int port = parsePort("ftp").value_or(-1); // запасной вариант, если пусто
* или -> - это неопределённое поведение, ровно как разыменование нулевого указателя. Всегда сначала проверяйте или используйте value_or. Если предпочитаете исключение, вызовите .value(), который бросает std::bad_optional_access, когда значения нет.