C++17: optional, variant, structured bindings

C++17 — optional, variant, structured bindings

Read: ~30 min 14 practice problems

1. std::optional<T>

"A T, or nothing" — a type-safe alternative to nullable values.

opt.cpp
#include <iostream>
#include <optional>

std::optional<int> parse(const std::string& s) {
    try { return std::stoi(s); }
    catch (...) { return std::nullopt; }
}

int main() {
    if (auto n = parse("42"); n)
        std::cout << "got " << *n << "\n";
    else
        std::cout << "failed\n";

    std::cout << parse("bad").value_or(-1) << "\n";
}

2. std::variant<...>

A type-safe union: holds exactly one of several types.

variant.cpp
#include <iostream>
#include <variant>
#include <string>

int main() {
    std::variant<int, std::string, double> v;
    v = 42;
    std::cout << std::get<int>(v) << "\n";

    v = std::string{"hello"};
    std::cout << std::get<std::string>(v) << "\n";

    std::visit([](const auto& val) {
        std::cout << "visiting: " << val << "\n";
    }, v);
}

3. Structured Bindings

bind.cpp
#include <map>
#include <iostream>

int main() {
    std::map<std::string, int> m = {{"a",1},{"b",2}};
    for (const auto& [k, v] : m)
        std::cout << k << "=" << v << "\n";

    // Works with structs too:
    struct Point { int x, y; };
    Point p{3, 4};
    auto [x, y] = p;
}

4. if constexpr

Compile-time if for templates — generates only the matching branch:

ifc.cpp
template<typename T>
void print(const T& v) {
    if constexpr (std::is_pointer_v<T>)
        std::cout << *v;
    else
        std::cout << v;
}

5. Other C++17 Highlights

  • std::filesystem — paths and FS ops
  • std::string_view — non-owning string
  • Mandatory copy elision
  • [[nodiscard]], [[maybe_unused]] attributes
  • Class template argument deduction (CTAD)

6. Practice Problems

  1. A function returning optional<double> for safe sqrt.
    ✨ Show Answer
    std::optional<double> safe_sqrt(double x) {
        if (x < 0) return std::nullopt;
        return std::sqrt(x);
    }
  2. Check if optional has value.
    ✨ Show Answer
    if (opt) { use(*opt); }
    if (opt.has_value()) { ... }
  3. Variant of int, string, vector.
    ✨ Show Answer
    std::variant<int, std::string, std::vector<int>> v;
  4. Get current type of variant.
    ✨ Show Answer
    v.index(); // 0 = first type, 1 = second, ...
  5. Use std::visit with a generic lambda.
    ✨ Show Answer
    std::visit([](const auto& x){ std::cout << x; }, v);
  6. Structured-bind a std::pair.
    ✨ Show Answer
    auto [first, second] = std::make_pair(1, 2);
  7. When is if constexpr useful?
    ✨ Show Answer

    In templates where some branches only compile for some T. Avoids SFINAE complexity.

  8. [[nodiscard]] on a function — what does it do?
    ✨ Show Answer

    Compiler warns if the return value is ignored. Useful for "you must check this!" results.

  9. CTAD example.
    ✨ Show Answer
    std::vector v = {1, 2, 3}; // deduces vector<int>
  10. Why optional over pointer?
    ✨ Show Answer

    Value type — no allocation, no nullability bugs by accident, clearer intent: "maybe-a-value".

  11. Use std::filesystem to check file existence.
    ✨ Show Answer
    #include <filesystem>
    if (std::filesystem::exists("data.txt")) { ... }
  12. Set optional to empty.
    ✨ Show Answer
    opt.reset(); // or opt = std::nullopt;
  13. Catch missing variant alternative.
    ✨ Show Answer

    std::get<Bad>(v) throws std::bad_variant_access. Or use std::get_if<T>(&v) which returns nullptr.

  14. Why prefer optional over -1 sentinel?
    ✨ Show Answer

    Type-safe, self-documenting, can't accidentally use -1 as a real value, works for any type (not just numerics).

Summary

C++17 added optional, variant, structured bindings, and if constexpr. Together they replace many ugly idioms (sentinels, unions, multi-returns). Plus filesystem, CTAD, and stricter copy elision. Adopt these aggressively in new code.

Next Module → C++20: Concepts, Ranges, Coroutines preview.