C++ Basics: Compose Lazy Filter and Transform Views
Build a C++20 range pipeline without creating an intermediate result vector.
Prepare a scratch directory
Tested on Linux x86_64 with GCC 14.2.0. Install a C++20-capable compiler and standard-library development headers, with g++ on PATH. You also need Bash, a text editor, and the mktemp, rm, and rmdir utilities. No third-party library is required.
Keep the same terminal open. Stop if setup fails, before saving the source:
start_directory="$PWD"
work_directory="$(mktemp -d)" || exit 1
cd -- "$work_directory" || exit 1Run the example
Save this as lazy-filter-transform-demo.cpp:
#include <iostream>
#include <ranges>
#include <vector>
int main() {
std::vector<int> values{1, 2, 3, 4};
auto doubled_even_values = values
| std::views::filter([](int value) { return value % 2 == 0; })
| std::views::transform([](int value) { return value * 2; });
values[1] = 6; // Change the source before any iteration begins.
std::cout << "View:";
for (int value : doubled_even_values) {
std::cout << ' ' << value;
}
std::cout << "\nSource:";
for (int value : values) {
std::cout << ' ' << value;
}
std::cout << '\n';
}Compile and run it. The second command runs only after compilation succeeds:
g++ -std=c++20 -Wall -Wextra -Werror -pedantic lazy-filter-transform-demo.cpp -o lazy-filter-transform-demo &&
./lazy-filter-transform-demoObserved output:
View: 12 8
Source: 1 6 3 4Read the result
The pipeline stores a description of the computation rather than a vector of answers. Changing two to six before iteration means the view yields twelve and eight. Transformation does not overwrite the source, which still holds six and four.
Apply it carefully
Keep the source alive while using this view. Do not mutate the vector while traversing it: reallocation can invalidate iterators, and filter views may cache their first match. The source change here occurs before the first begin() call and preserves element count. Keep predicates and transformations free of state-dependent side effects; a view is not a promise of one invocation per element across arbitrary uses.
The C++ working draft describes the relevant library rules. This example uses only C++20 facilities, even though the linked draft also documents newer additions.
Clean up
Remove only the source and executable created here, then remove the empty practice directory:
rm -- lazy-filter-transform-demo.cpp lazy-filter-transform-demo
cd -- "$start_directory" || exit 1
rmdir -- "$work_directory"
unset start_directory work_directoryIf the directory is not empty, inspect its remaining contents before removing anything else.
Acknowledgements
This post was created with assistance from AI.