CodeNebula
C++20stladvanced

std::ranges::borrowed_range (C++20 Addition)

C++20 · The Big Four · Compiler flag: -std=c++20

Explanation

Concept for ranges whose iterators remain valid even after the range object is destroyed. Prevents dangling iterators when algorithms return iterators into temporary ranges.

Real-World Scenario

Safely return an iterator from a function that takes a temporary span or string_view.

Quick Snippet

snippet.cpp
#include <ranges>
#include <span>

// std::span is a borrowed_range, so this is safe:
auto find_in_span(std::span<const int> data, int val) {
    return std::ranges::find(data, val);  // iterator remains valid
}

Full Example

ranges_borrowed_range.cpp
// CodeNebula - C++20 Example: std::ranges::borrowed_range Concept
// Compile: g++ -std=c++20 -Wall -Wextra -o example ranges_borrowed_range.cpp

#include <ranges>
#include <vector>
#include <string>
#include <span>
#include <algorithm>
#include <iostream>

// std::ranges::borrowed_range indicates that iterators from a range
// remain valid even after the range object is destroyed. This prevents
// dangling iterator bugs at compile time.

// Safe function: returns iterator only from borrowed ranges
template <std::ranges::borrowed_range R>
auto safe_find(R&& r, const std::ranges::range_value_t<R>& val) {
    return std::ranges::find(r, val);
}

int main() {
    // Views and spans are borrowed ranges
    static_assert(std::ranges::borrowed_range<std::span<int>>);
    static_assert(std::ranges::borrowed_range<std::string_view>);

    // Owning containers are NOT borrowed (iterators dangle if container dies)
    static_assert(!std::ranges::borrowed_range<std::vector<int>>);
    static_assert(!std::ranges::borrowed_range<std::string>);

    // Safe: finding in an lvalue container
    std::vector<int> vec = {10, 20, 30, 40, 50};
    auto it = std::ranges::find(vec, 30);
    if (it != vec.end()) {
        std::cout << "Found: " << *it << "\n";
    }

    // ranges::find on rvalue non-borrowed range returns ranges::dangling
    auto result = std::ranges::find(std::vector{1, 2, 3}, 2);
    static_assert(std::same_as<decltype(result), std::ranges::dangling>);
    std::cout << "Rvalue vector find returns dangling (compile-time safety)\n";

    // span is borrowed - safe to return iterators from rvalue
    int arr[] = {5, 10, 15, 20};
    auto sp_result = std::ranges::find(std::span(arr), 15);
    std::cout << "Found in span: " << *sp_result << "\n";

    // string_view is borrowed
    auto sv_result = std::ranges::find(std::string_view("hello"), 'l');
    std::cout << "Found in string_view: " << *sv_result << "\n";

    // iota_view is borrowed
    auto iota = std::views::iota(1, 10);
    static_assert(std::ranges::borrowed_range<decltype(iota)>);
    auto iota_it = safe_find(iota, 7);
    std::cout << "Found in iota: " << *iota_it << "\n";

    // Subrange is borrowed
    auto sub = std::ranges::subrange(vec.begin() + 1, vec.begin() + 4);
    static_assert(std::ranges::borrowed_range<decltype(sub)>);
    std::cout << "Subrange: ";
    for (int n : sub) std::cout << n << " ";
    std::cout << "\n";

    return 0;
}
Run command
g++ -std=c++20 -Wall -Wextra -o example ranges_borrowed_range.cpp