Skip to content

Vocabulary Types & Utilities

C++17 introduced several vocabulary types and utilities that make code safer and more expressive, reducing the need for custom error handling or complex type hierarchies.

std::optional (Maybe a Value)

std::optional<T> represents a value that may or may not be present. It is a safer alternative to returning pointers (which can be null) or magic values (like -1).

#include <optional>

std::optional<int> parse_int(const std::string& str) {
    if (is_valid_number(str)) {
        return std::stoi(str);
    }
    return std::nullopt; // No value
}

auto result = parse_int("123");
if (result) { // Check if value exists
    std::cout << "Value: " << *result; // Access value
} else {
    std::cout << "Invalid number";
}

// Use default value if empty
int value = result.value_or(0); 

std::variant (Type-Safe Union)

std::variant<T, U, ...> can hold a value of one of the specified types. It is a type-safe replacement for C-style union.

#include <variant>

std::variant<int, float, std::string> data;

data = 10;
data = 3.14f;
data = "Hello";

// Accessing values
if (std::holds_alternative<std::string>(data)) {
    std::cout << std::get<std::string>(data);
}

// Visiting (Pattern Matching)
std::visit([](auto&& arg) {
    std::cout << arg;
}, data);

std::any (Type Erasure)

std::any can hold a value of any copyable type. It is more flexible than variant but less efficient and requires any_cast to retrieve the value.

#include <any>

std::any a = 1;
a = std::string("Hello");

try {
    std::string s = std::any_cast<std::string>(a);
} catch (const std::bad_any_cast& e) {
    std::cout << e.what();
}

std::expected (C++23)

std::expected<T, E> represents either a value of type T (success) or an error of type E (failure). It is cleaner than exceptions for expected errors.

#include <expected>
#include <iostream>

enum class Error { DivideByZero };

std::expected<int, Error> divide(int a, int b) {
    if (b == 0) return std::unexpected(Error::DivideByZero);
    return a / b;
}

auto result = divide(10, 0);
if (result) {
    std::cout << "Result: " << *result;
} else {
    std::cout << "Error occurred";
}

Filesystem Library (std::filesystem)

The <filesystem> library provides a portable way to interact with the file system.

#include <filesystem>
namespace fs = std::filesystem;

void list_files(const fs::path& dir_path) {
    if (fs::exists(dir_path) && fs::is_directory(dir_path)) {
        for (const auto& entry : fs::directory_iterator(dir_path)) {
            std::cout << entry.path().filename() << "\n";
        }
    }
}

// Creating directories
fs::create_directory("logs");

// Copying files
fs::copy_file("config.txt", "config.bak", fs::copy_options::overwrite_existing);