std::span


Keyword: std::span (C++20)

std::span is a non-owning view of a contiguous sequence of objects, introduced in C++20. It provides a lightweight, non-owning reference to a contiguous sequence of elements, offering a safer and more flexible alternative to pointer and size pairs. std::span is particularly useful for passing array-like data to functions without copying and for working with subranges of containers.

Example 1: Basic Usage with Arrays

#include <iostream>
#include <span>

void printNumbers(std::span<const int> numbers) {
    for (const auto& num : numbers) {
        std::cout << num << ' ';
    }
    std::cout << '\n';
}

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    printNumbers(arr);
    return 0;
}

Explanation

Example 2: Working with Vectors

#include <iostream>
#include <span>
#include <vector>

void modifyFirstHalf(std::span<int> numbers) {
    auto firstHalf = numbers.first(numbers.size() / 2);
    for (auto& num : firstHalf) {
        num *= 2;
    }
}

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5, 6};
    std::cout << "Original vector: ";
    for (int num : vec) std::cout << num << ' ';
    std::cout << '\n';

    modifyFirstHalf(vec);

    std::cout << "Modified vector: ";
    for (int num : vec) std::cout << num << ' ';
    std::cout << '\n';

    return 0;
}

Explanation

Example 3: Fixed-Size Spans

#include <iostream>
#include <span>
#include <array>

void processCoordinates(std::span<const double, 3> coord) {
    std::cout << "X: " << coord[0] << ", Y: " << coord[1] << ", Z: " << coord[2] << '\n';
}

int main() {
    std::array<double, 3> point3D = {1.0, 2.0, 3.0};
    processCoordinates(point3D);

    double rawPoint[] = {4.0, 5.0, 6.0};
    processCoordinates(rawPoint);

    // Uncommenting the following line would result in a compile-time error
    // std::array<double, 4> point4D = {1.0, 2.0, 3.0, 4.0};
    // processCoordinates(point4D);  // Error: wrong number of elements

    return 0;
}

Explanation

Example 4: Dynamic Spans with Runtime Checks

#include <iostream>
#include <span>
#include <vector>
#include <stdexcept>

void processData(std::span<int> data) {
    if (data.size() < 2) {
        throw std::runtime_error("Data must contain at least 2 elements");
    }
    data[0] = data[data.size() - 1];
    std::cout << "First element set to: " << data[0] << '\n';
}

int main() {
    try {
        std::vector<int> vec1 = {1, 2, 3, 4, 5};
        processData(vec1);

        std::vector<int> vec2 = {10};
        processData(vec2);  // This will throw an exception
    }
    catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << '\n';
    }

    return 0;
}

Explanation

Summary

std::span is a powerful addition to the C++ standard library, offering a safe and efficient way to work with contiguous sequences of objects. It provides several key benefits:

  1. Non-owning view: std::span doesn't own the data it refers to, making it lightweight and efficient.
  2. Flexibility: It can work with various contiguous containers like arrays, std::array, and std::vector.
  3. Safety: Fixed-size spans provide compile-time checks, while dynamic spans allow for runtime checks.
  4. Performance: std::span avoids unnecessary copying of data.
  5. Convenience: It simplifies function interfaces that work with array-like data.

By using std::span, C++ developers can write more robust and efficient code when dealing with contiguous sequences of objects, improving both safety and performance in their applications.

Related

Previous Page | Course Schedule | Course Content