# Vocabulary Types: optional, variant, string_view and span — C++

Source: https://www.skillbyai.com/en/cpp/s-utilities

> Use modern vocabulary types to express intent safely.

## Types that say what you mean

Modern C++ added small "vocabulary" types that make interfaces clearer and safer. **`std::optional<T>`** (C++17) represents a value that may be absent, replacing sentinel values such as -1 or null pointers: `std::optional<User> find_user(int id)`. **`std::variant<A, B, C>`** (C++17) holds exactly one of several types, a type-safe union; inspect it with `std::visit` or `std::holds_alternative`. **`std::string_view`** (C++17) is a non-owning view of characters, ideal for read-only string parameters because it accepts `std::string`, literals and substrings without copying; it must not outlive the data it refers to. **`std::span<T>`** (C++20) is a non-owning view of a contiguous sequence (vector, array, C array). **`std::pair`** and **`std::tuple`** group values, and **structured bindings** (`auto [name, marks] = pair;`) unpack them. **`std::expected<T, E>`** (C++23) returns either a value or an error. Using these types documents intent in the type system instead of comments.

## Vocabulary types in function interfaces

Absence, alternatives and views are explicit in the signatures.

```cpp
#include <optional>
#include <span>
#include <string>
#include <string_view>
#include <type_traits>
#include <variant>
#include <vector>

std::optional<int> parse_marks(std::string_view text) {    // accepts literals and strings, no copy
    int value = 0;
    for (char ch : text) {
        if (ch < '0' || ch > '9') return std::nullopt;     // explicit "no value"
        value = value * 10 + (ch - '0');
    }
    return value <= 100 ? std::optional<int>{value} : std::nullopt;
}

double average(std::span<const int> values) {               // vector, array or C array
    double sum = 0;
    for (int v : values) sum += v;
    return values.empty() ? 0.0 : sum / values.size();
}

using Payment = std::variant<std::string /* UPI id */, long long /* card last digits */>;

std::string describe(const Payment& p) {
    return std::visit([](const auto& v) -> std::string {
        if constexpr (std::is_same_v<std::decay_t<decltype(v)>, std::string>) return "UPI " + v;
        else return "card ending " + std::to_string(v);
    }, p);
}

int main() {
    if (auto m = parse_marks("87")) { /* *m is 87 */ }
    std::vector<int> v{70, 80, 90};
    double avg = average(v);
}
```

## Views must not outlive their data

Returning a `std::string_view` or `std::span` that refers to a local string or vector creates a dangling view. Use views for parameters; return owning types such as `std::string` and `std::vector`.

**Quiz:** Which type best expresses "a function may or may not return an integer"?

- [ ] int with -1 as a special value
- [ ] std::variant<int, int>
- [x] std::optional<int>
- [ ] int*

*Answer:* std::optional<int>. std::optional makes absence explicit in the type instead of relying on magic values.
