Skip to content

Vocabulary Types: optional, variant and tuple

Beginning of the lesson

Vocabulary Types: optional, variant and tuple

Type-safe tools for maybe-a-value, one-of-several-types, and grouping

Big Idea: The standard library ships a handful of small, general-purpose types that show up in the signatures of countless real libraries. They are called vocabulary types because they give everyone a shared vocabulary for common shapes of data: "a value that might be absent", "a value that is one of several types", and "a fixed bundle of a few values". Using them is how modern C++ replaces the old, error-prone tricks: sentinel return values, output parameters, and raw union.
By the end you will be able to:
  • use std::optional<T> to model a value that may or may not be present,
  • use std::variant<A, B, ...> as a type-safe union that always knows which type it holds,
  • group a few values with std::pair and std::tuple,
  • unpack any of them cleanly with structured bindings.

This topic assumes you are comfortable with templates, references and move semantics from earlier in this course. All examples compile with a C++17 compiler.

1. std::optional - maybe a value

A function often needs to say "here is a result" or "there is no result". The old ways of expressing that are all fragile: return a magic sentinel like -1 (what if -1 is a valid answer?), return a bool and write the real answer through an output parameter, or return a raw pointer that might be nullptr (who owns it?). std::optional<T> (from <optional>) makes "absent" a first-class, type-checked state.

An optional<T> either contains a T or is empty. You test it like a bool, read the value with * or -> (only when it is engaged), and supply a fallback with value_or. The empty state is written 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;                 // no sensible value to return
}

The caller cannot forget to check, because the value lives inside the optional and you must open it first:

auto p = parsePort("https");
if (p) {                            // is it engaged?
    cout << "port = " << *p << "\n";  // safe: we checked
}
int port = parsePort("ftp").value_or(-1);  // fallback if empty
Dereferencing an empty optional with * or -> is undefined behaviour, exactly like dereferencing a null pointer. Always check first, or use value_or. If you prefer an exception instead, call .value(), which throws std::bad_optional_access when empty.

Continue this lesson on YourAIZone

The full lesson has the complete explanation, worked examples, common mistakes, tests with instant checking and an AI tutor. Start right away as a guest, or create an account to keep your progress.

In the full lesson

  • The complete explanation
  • Worked examples
  • Common mistakes and how to avoid them
  • Test questions: 15
  • AI tutor that answers questions about the topic
  • Progress tracking for students, parents and teachers