C++ Views | Lazy Range Views in C++20

C++ Views | Lazy Range Views in C++20

이 글의 핵심

Practical guide to C++ views: concepts, lazy pipelines, and production use with examples.

What are views?

Lazy-evaluated ranges (C++20).

#include <ranges>

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

// View: no copy
auto filtered = v | std::views::filter([](int x) { return x > 2; });

// Evaluated on iteration
for (int x : filtered) {
    std::cout << x << " ";  // 3 4 5
}

Basic views

namespace vws = std::views;

// filter: predicate
vws::filter([](int x) { return x % 2 == 0; })

// transform: map
vws::transform([](int x) { return x * 2; })

// take: first n elements
vws::take(5)

// drop: skip first n
vws::drop(3)

// reverse
vws::reverse

Practical examples

Example 1: filter

#include <ranges>
#include <vector>

std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// Evens only
auto evens = numbers | std::views::filter([](int x) { 
    return x % 2 == 0; 
});

for (int x : evens) {
    std::cout << x << " ";  // 2 4 6 8 10
}

Example 2: transform

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

// Squares
auto squares = numbers | std::views::transform([](int x) { 
    return x * x; 
});

for (int x : squares) {
    std::cout << x << " ";  // 1 4 9 16 25
}

Example 3: take & drop

std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// First 5
auto first5 = numbers | std::views::take(5);
// 1 2 3 4 5

// Skip first 3
auto skip3 = numbers | std::views::drop(3);
// 4 5 6 7 8 9 10

// Combined: skip 3, then take 5
auto middle = numbers | std::views::drop(3) | std::views::take(5);
// 4 5 6 7 8

Example 4: composite pipeline

#include <ranges>
#include <vector>
#include <string>

struct Person {
    std::string name;
    int age;
};

std::vector<Person> people = {
    {"Alice", 25},
    {"Bob", 30},
    {"Charlie", 35},
    {"David", 40}
};

// Age >= 30 -> names -> uppercase -> first 2
auto result = people
    | std::views::filter([](const Person& p) { return p.age >= 30; })
    | std::views::transform([](const Person& p) { return p.name; })
    | std::views::transform([](std::string name) {
        std::string upper = name;
        for (char& c : upper) c = std::toupper(c);
        return upper;
    })
    | std::views::take(2);

for (const auto& name : result) {
    std::cout << name << std::endl;  // BOB, CHARLIE
}

Conditional views

namespace vws = std::views;

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

// take_while: while predicate holds
auto lessThan5 = v | vws::take_while([](int x) { return x < 5; });
// 1 2 3 4

// drop_while: skip while predicate holds
auto from5 = v | vws::drop_while([](int x) { return x < 5; });
// 5 6 7 8 9 10

Common problems

Problem 1: Lifetime

// ❌ Dangling view
auto getView() {
    std::vector<int> v = {1, 2, 3};
    return v | std::views::filter([](int x) { return x > 1; });
    // v destroyed
}

// ✅ Own the result
auto getFiltered() {
    std::vector<int> v = {1, 2, 3};
    std::vector<int> result;
    auto view = v | std::views::filter([](int x) { return x > 1; });
    std::ranges::copy(view, std::back_inserter(result));
    return result;
}

Problem 2: Aliasing / mutation

// Views do not copy the sequence
std::vector<int> v = {1, 2, 3};
auto view = v | std::views::transform([](int x) { return x * 2; });

v[0] = 10;

// view still refers to v
for (int x : view) {
    std::cout << x << " ";  // 20 4 6
}

Problem 3: Re-evaluation

auto view = v | std::views::filter(pred) | std::views::transform(func);

// Recomputed each pass
for (int x : view) { /* ... */ }  // compute
for (int x : view) { /* ... */ }  // compute again

// ✅ Cache if needed
std::vector<int> cached(view.begin(), view.end());

Problem 4: Pipeline order

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

// ❌ Less efficient
auto r1 = v
    | std::views::transform([](int x) { return x * x; })  // 10 squares
    | std::views::take(3);  // use only 3

// ✅ Better order
auto r2 = v
    | std::views::take(3)  // 3 elements only
    | std::views::transform([](int x) { return x * x; });  // 3 squares

Combining views

namespace vws = std::views;

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

// Multiple adapters
auto result = v
    | vws::filter([](int x) { return x % 2 == 0; })  // evens
    | vws::transform([](int x) { return x * x; })    // squares
    | vws::reverse                                    // reverse
    | vws::take(3);                                   // first 3

// 100 64 36

FAQ

Q1: What are views?

A: Lazily evaluated range adapters.

Q2: Lazy evaluation?

A: Computation happens when you iterate.

Q2: Copying?

A: Views do not copy elements; they refer to the source.

Q4: Pipelines?

A: Combine with the | operator.

Q5: Performance?

A:

  • No element copy by default
  • Lazy evaluation
  • Order of adapters matters

Q6: Learning resources?

A:

  • “C++20 Ranges”
  • “C++20 The Complete Guide”
  • cppreference.com

Other posts that connect to this topic.

  • C++ Ranges | range library guide
  • C++ Ranges | functional-style C++20 guide
  • C++ subrange | partial range guide

Practical tips

Tips you can apply at work.

Debugging

  • When something breaks, check compiler warnings first
  • Reproduce with a small test case

Performance

  • Do not optimize without profiling
  • Define measurable targets first

Code review

  • Pre-check areas that often get flagged in review
  • Follow team conventions

Production checklist

Things to verify when applying this idea in practice.

Before coding

  • Is this technique the best fit for the problem?
  • Can teammates understand and maintain it?
  • Does it meet performance requirements?

While coding

  • Are all compiler warnings addressed?
  • Are edge cases considered?
  • Is error handling appropriate?

At review

  • Is intent clear?
  • Are tests sufficient?
  • Is it documented?

Use this checklist to reduce mistakes and improve quality.


Keywords covered

Search for C++, views, ranges, lazy, C++20 to find this post.


  • C++ Ranges pipeline & views
  • C++ Ranges basics
  • C++ Generator guide | co_yield, lazy sequences
  • C++20 Ranges basics series
  • C++ Ranges views and pipelines series