Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. C++ Dictionary
  3. ranges

ranges

Introduced in C++20, std::ranges is a revamped namespace of STL algorithms that allows safer, more concise code. Using the pipe operator | for view composition, you can declaratively chain multiple transformations and filter operations. Unlike traditional algorithms that require iterator pairs, std::ranges algorithms accept containers directly, eliminating iterator mismatch bugs at the source.

Syntax

// ========================================
// std::ranges basics
// ========================================

#include <ranges>
#include <algorithm>
#include <vector>

std::vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6};

// ---- Traditional std algorithm (requires iterator pair) ----
std::sort(v.begin(), v.end());

// ---- std::ranges algorithm (accepts container directly) ----
std::ranges::sort(v);

// ---- Views: chain transformations with the pipe operator ----
// Multiple views can be composed with |. Lazy evaluation keeps it efficient.
auto result = v
    | std::views::filter([](int x) { return x % 2 == 0; })  // keep even numbers
    | std::views::transform([](int x) { return x * 10; });  // multiply by 10

// ---- Commonly used views ----
std::views::filter(pred)      // passes only elements matching the predicate
std::views::transform(func)   // maps each element through a function
std::views::take(n)           // takes only the first n elements
std::views::drop(n)           // skips the first n elements
std::views::reverse           // reverses the order
std::views::iota(start, end)  // generates a sequence of integers (no container needed)

Key Views and Algorithms

NameDescription
std::ranges::sortSorts a container directly. No iterator pair needed.
std::ranges::findSearches a container for a value and returns an iterator.
std::ranges::find_ifReturns the first element satisfying a predicate.
std::ranges::count_ifReturns the count of elements satisfying a predicate.
std::ranges::for_eachApplies a function to each element.
std::ranges::copyCopies a range to another container.
std::views::filterCreates a view that passes only elements for which the predicate is true.
std::views::transformCreates a view that maps each element through a transformation function.
std::views::take(n)Creates a view of only the first n elements.
std::views::drop(n)Creates a view that skips the first n elements.
std::views::reverseCreates a view with elements in reverse order.
std::views::iota(a, b)Generates a view of integers from a to b-1. No container required.
std::views::keys / valuesCreates a view of keys or values from map-like pairs.
std::ranges::to<T>()Collects a view into container T (C++23, available from gcc13+).

Sample Code

kof_ranges.cpp
// ========================================
// kof_ranges.cpp
// Demonstrates std::ranges with KOF
// (The King of Fighters) character data:
// filtering, transforming, and sorting
// ========================================

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

// ========================================
// Fighter struct
// ========================================
struct Fighter {
    std::string name;
    std::string team;
    int power;
    bool isMain;
};

int main() {
    std::vector<Fighter> roster = {
        {"Kusanagi Kyo",  "Kusanagi Team", 9200, true},
        {"Yagami Iori",   "Yagami Team",   9500, true},
        {"Terry Bogard",  "Bogard Team",   8800, true},
        {"Shiranui Mai",  "Bogard Team",   8200, false},
        {"King",          "Art of Fighting Team", 8100, false},
        {"Billy Kane",    "Yagami Team",   7500, false},
    };

    // ========================================
    // 1. std::ranges::sort — sort by power descending
    //    Pass the container directly (no iterator pair needed)
    //    Third arg is comparator, fourth is projection
    // ========================================
    std::cout << "=== Sorted by power (descending) ===" << std::endl;

    std::vector<Fighter> sorted = roster;
    std::ranges::sort(sorted, std::greater<int>{}, &Fighter::power);

    for (const auto& f : sorted) {
        std::cout << "  " << f.name << "  power: " << f.power << std::endl;
    }

    // ========================================
    // 2. views::filter — keep only main characters
    // ========================================
    std::cout << std::endl << "=== Main characters only ===" << std::endl;

    auto mainFighters = roster
        | std::views::filter([](const Fighter& f) { return f.isMain; });

    for (const auto& f : mainFighters) {
        std::cout << "  " << f.name << " (" << f.team << ")" << std::endl;
    }

    // ========================================
    // 3. views::transform — extract names
    // ========================================
    std::cout << std::endl << "=== All character names ===" << std::endl;

    auto names = roster
        | std::views::transform([](const Fighter& f) { return f.name; });

    for (const auto& name : names) {
        std::cout << "  " << name << std::endl;
    }

    // ========================================
    // 4. Composed pipeline
    //    Filter Bogard Team with power >= 8100
    //    then transform to a label string
    // ========================================
    std::cout << std::endl << "=== Bogard Team, power >= 8100 ===" << std::endl;

    auto elite = roster
        | std::views::filter([](const Fighter& f) {
            return f.team == "Bogard Team" && f.power >= 8100;
          })
        | std::views::transform([](const Fighter& f) {
            return f.name + "  [" + std::to_string(f.power) + "]";
          });

    for (const auto& label : elite) {
        std::cout << "  " << label << std::endl;
    }

    // ========================================
    // 5. views::iota — generate a sequence without a container
    // ========================================
    std::cout << std::endl << "=== Round numbers (odd rounds 1-5) ===" << std::endl;

    auto oddRounds = std::views::iota(1, 6)
        | std::views::filter([](int n) { return n % 2 != 0; });

    for (int r : oddRounds) {
        std::cout << "  ROUND " << r << std::endl;
    }

    // ========================================
    // 6. std::ranges::count_if — aggregate with a condition
    // ========================================
    std::cout << std::endl << "=== Characters with power >= 8500 ===" << std::endl;

    auto topCount = std::ranges::count_if(roster, [](const Fighter& f) {
        return f.power >= 8500;
    });
    std::cout << "  " << topCount << " fighter(s)" << std::endl;

    // ========================================
    // 7. std::ranges::find_if — search
    // ========================================
    std::cout << std::endl << "=== First member of Yagami Team ===" << std::endl;

    auto it = std::ranges::find_if(roster, [](const Fighter& f) {
        return f.team == "Yagami Team";
    });

    if (it != roster.end()) {
        std::cout << "  Found: " << it->name
                  << "  power: " << it->power << std::endl;
    }

    return 0;
}
g++ -std=c++20 kof_ranges.cpp -o kof_ranges && ./kof_ranges
=== Sorted by power (descending) ===
  Yagami Iori  power: 9500
  Kusanagi Kyo  power: 9200
  Terry Bogard  power: 8800
  Shiranui Mai  power: 8200
  King  power: 8100
  Billy Kane  power: 7500

=== Main characters only ===
  Kusanagi Kyo (Kusanagi Team)
  Yagami Iori (Yagami Team)
  Terry Bogard (Bogard Team)

=== All character names ===
  Kusanagi Kyo
  Yagami Iori
  Terry Bogard
  Shiranui Mai
  King
  Billy Kane

=== Bogard Team, power >= 8100 ===
  Terry Bogard  [8800]
  Shiranui Mai  [8200]

=== Round numbers (odd rounds 1-5) ===
  ROUND 1
  ROUND 3
  ROUND 5

=== Characters with power >= 8500 ===
  2 fighter(s)

=== First member of Yagami Team ===
  Found: Yagami Iori  power: 9500

Common mistake 1: Reusing a view after modifying the underlying container

Views (results of std::views::filter etc.) are lazy evaluation objects, not containers. Modifying the underlying container after creating a view may lead to undefined behavior. To capture a fixed result, copy the view into a container.

// NG:
std::vector<int> v = {9500, 9200, 8800};
auto view = v | std::views::filter([](int x) { return x > 9000; });
v.push_back(9100);  // modifying the container may invalidate the view
for (int x : view) { /* dangerous */ }

OK: Copy the view into a container first if you need a stable result.

// OK:
std::vector<int> v = {9500, 9200, 8800};
auto view = v | std::views::filter([](int x) { return x > 9000; });
std::vector<int> result(view.begin(), view.end());  // copy into a container
// you can modify v after this without affecting result

Common mistake 2: Confusing std::sort with std::ranges::sort

The traditional std::sort takes an iterator pair, while std::ranges::sort accepts a container directly. Mixing them causes a compile error. The projection argument is also exclusive to std::ranges::sort.

// NG:
struct Fighter { std::string name; int power; };
std::vector<Fighter> roster = {{"Yagami Iori", 9500}, {"Kusanagi Kyo", 9200}};
std::sort(roster, std::greater<int>{}, &Fighter::power);  // std::sort does not accept a projection

OK: Use std::ranges::sort for projections.

// OK:
std::ranges::sort(roster, std::greater<int>{}, &Fighter::power);  // projection works here

Overview

std::ranges is the C++20 namespace that redesigns the traditional STL algorithms with direct container support and concept constraints. The biggest benefits are improved readability from dropping iterator pairs and the ability to compose views via the pipe operator |. Views such as std::views::filter and std::views::transform use lazy evaluation: building the pipeline does not process any data. Computation occurs only when elements are actually read, so even large collections are handled without creating intermediate arrays. The projection argument lets you write std::ranges::sort(v, cmp, &Struct::member) to sort directly by a member variable, eliminating the need for a verbose lambda. std::views::iota generates integer sequences without a container, making it handy for loop indices. Include <ranges> and compile with the -std=c++20 flag. See also STL algorithms and lambda expressions.

If you find any errors or copyright issues, please .