ReviseAlgo Logo

Standard Template Library (STL)

std::optional and std::variant

C++17 sum types — optional values and type-safe tagged unions

Interview: Modern error handling and type safety — shows knowledge of C++17 value semantics

std::optional and std::variant

These C++17 types encode semantic intent in the type system rather than conventions: std::optional<T> explicitly represents "T or nothing", and std::variant<T1, T2...> represents "exactly one of these types".

std::optional

Replaces returning -1, nullptr, or a boolean out-parameter to signal "no value". The caller is forced to check before use. Access: opt.value() (throws if empty), opt.value_or(default), or dereference *opt after checking opt.has_value() or if (opt). No heap allocation — stores the value inline.

std::variant

A type-safe union. Always holds exactly one of its declared types. Unlike C unions, accessing the wrong type throws std::bad_variant_access. Access with std::get<T>(v) or pattern-match with std::visit. Common use: representing AST nodes, command results, or error/success values.

std::any

std::any holds a value of any type (with type erasure) — fully dynamic. Use when the type is truly unknown at compile time. Prefer variant when the possible types are known at compile time — it's type-safe and more efficient.

Interview Corner

Q: Why is std::optional better than returning nullptr or -1?

A: (1) Self-documenting: the function signature explicitly says "may return nothing". (2) Type-safe: can be used with any type, not just pointer types. (3) Forces handling: callers must explicitly check or use value_or(), rather than silently dereferencing a null pointer. (4) No heap allocation: the value is stored inline in the optional object.

Common Pitfalls

  • Dereferencing empty optional: *emptyOpt is undefined behavior. Use value() (throws) or check has_value() first.
  • Accessing wrong variant type: get<T>(v) throws bad_variant_access if v doesn't hold T. Use std::visit for safe exhaustive handling.

Best Practices

  • Use optional for functions that might not produce a result — it's cleaner than sentinel values and forces callers to handle the empty case.
  • Use std::visit with a visitor that handles all types for exhaustive variant processing — the compiler enforces you handle all alternatives.