Modern Types, Views, and Conversions
Standard Library Containers and Iterators

6.7 Modern Types, Views, and Conversions

Modern C++ adds type tools that reduce boilerplate without giving up safety, if you know where they hide footguns. This lesson ties together auto, views, exact-width integers, and explicit conversions used throughout the rest of the course and in capstone 10.4.

auto: convenience with rules

Use auto when the initializer makes the type obvious (auto it = vec.begin();) or when spelling a long type adds noise. auto drops top-level const and references unless you write const auto&, auto&, or auto&& deliberately.

std::vector<int> values{1, 2, 3};
auto copy = values[0];        // int
auto& ref = values[0];        // int&
const auto& cref = values[0]; // const int&
auto preserves value category when you ask it to.

Structured bindings

Structured bindings unpack tuple-like types into named variables. Bindings can be by value, reference, or const reference, lifetime rules follow what you bind to.

#include <map>
#include <string>

std::map<int, std::string> users{{1, "Ada"}};
for (const auto& [id, name] : users) {
    // id is const int&, name is const std::string&
}
Structured bindings from std::pair.

std::optional and std::variant (recap)

Lesson 4.5 introduced std::variant for type-safe runtime dispatch without virtual tables. Pair it with std::optional<T> when a computation may legitimately produce no value, prefer that over sentinel integers or null pointers.

#include <optional>
#include <string>

std::optional<int> parsePort(const std::string& text) {
    if (text.empty()) return std::nullopt;
    return std::stoi(text); // narrow demo; validate errors in production
}
optional expresses missing data explicitly.

Exact-width integers for binary work

For wire formats, checksums, and hardware registers, use <cstdint> types (std::uint32_t, std::int64_t, etc.) when you need a documented width. For everyday loop indices, prefer the container's size() type or std::size_t and compare signed/unsigned deliberately, Lesson 1.3 showed overflow pitfalls.

std::span: bounded non-owning ranges

std::span<T> is a pointer plus length to contiguous caller-owned storage. It replaces the error-prone (T* data, size_t n) pattern and accepts std::array, C arrays, and std::vector without copying.

#include <array>
#include <cstdint>
#include <numeric>
#include <span>
#include <vector>

std::int64_t sum(std::span<const int> values) {
    return std::accumulate(values.begin(), values.end(), std::int64_t{0});
}

int main() {
    const std::array arr{4, 8, 15, 16};
    std::vector<int> vec{1, 2, 3};
    return sum(arr) + sum(vec) == 49 ? 0 : 1;
}
span accepts multiple contiguous owners.

std::string_view lifetime (critical)

std::string_view is a non-owning view of characters. It is fast for parameters, but every view must point at storage that outlives the view. Returning a string_view to a local std::string is the same dangling bug as Lesson 3.4, rewrite with an owning std::string or caller-provided buffer.

Explicit conversions

Use static_cast for checked, intentional conversions you can explain (numeric widening/narrowing after validation). Avoid reinterpret_cast for casual type punning, Lesson 3.6 shows std::bit_cast for same-size representations. Do not chain implicit conversions through several types; make each conversion visible at the call site.

Note

[[nodiscard]] on functions returning std::optional, error types, or handles reminds callers not to drop return values. It does not change runtime behavior, but it catches ignored failures in -Wall builds.

Practice: write std::optional<std::string_view> findToken(std::string_view haystack, std::string_view needle) that returns a substring view into haystack. Explain why the view remains valid after the function returns.

Chapter 6 Knowledge CheckQuestion 1 of 5

Which statement about Small String Optimization (SSO) is accurate?

Finished reading this lesson?