Last Update:
How to Split Ranges in C++23 and C++26

Table of Contents
In this blog post, we’ll continue looking at ranges and this time explore ways to split them into sub-ranges. So we’ll take a look at views::split
, views::chunk
, and views::chunk_by
.
We’ll walk through two examples for each adaptor: one simple and one slightly more advanced, to highlight their practical uses.
Let’s go.
Splitting Ranges with views::split
, C++20
If you want to split a range using some “delimeter,” then views::split
(or ranges::split_view
) will do the job.
template< ranges::forward_range V, ranges::forward_range Pattern >
// .. requires...
class split_vie : public ranges::view_interface<split_view<V, Pattern>>
Both of the ranges V
and Pattern
have to be forward_range
.
Notice that you need to use a pattern; it cannot be a condition.
Example 1: Splitting a Sentence into Words
#include <print>
#include <ranges>
#include <string_view>
int main() {
using namespace std::string_view_literals;
constexpr auto text = "C++ is powerful and elegant"sv;
for (auto part : std::views::split(text, ' '))
std::print("'{}' ", std::string_view(part));
}
Play @Compiler Explorer
The output:
'C++' 'is' 'powerful' 'and' 'elegant'
A classic example that splits a sentence into words. We convert each subrange to std::string_view
for easy printing.
We can also extend this and use not just one character:
constexpr auto text = "C++breakisbreakpowerfulbreakandbreakelegant"sv;
for (auto part : std::views::split(text, "break"sv))
std::print("'{}' ", std::string_view(part));
Example 2: Splitting Not Just Text
split
can be applied to any forward range, so not just text:
#include <print>
#include <ranges>
#include <vector>
#include <utility>
int main() {
using Point = std::pair<int, int>;
std::vector<Point> path = {
{0, 0}, {1, 1}, {-1, -1}, // marker: {-1, -1}
{2, 2}, {3, 3}, {-1, -1},
{4, 4}, {5, 5}
};
for (auto segment : std::views::split(path, Point{-1, -1}))
std::print("Segment: {}\n", segment);
}
And the output:
Segment: [(0, 0), (1, 1)]
Segment: [(2, 2), (3, 3)]
Segment: [(4, 4), (5, 5)]
In the example, we run through the point list and break at markers that are (-1, -1)
. This code also leveraged std::format
support for ranges (C++23), so we don’t have to print individual points.
What About lazy_split
?
You might also encounter views::lazy_split
, introduced in C++20.
This view is specialized for input-only ranges, such as reading from streams or generators.
It avoids buffering or requiring multiple passes, but its subranges aren’t contiguous and don’t expose .data()
or .size()
.
Unless you’re processing streams in a single pass, prefer views::split
for simplicity.
Here’s a question from Stack Overflow that I recently asked to clarify this view: c++ - What is the use of std::ranges::views::lazy_split when we have std::ranges::views::split? - Stack Overflow
Grouping with views::chunk
, C++23
When you need to process data in fixed-size batches, chunk
is the perfect tool.
Example 1: Fixed-Size Batches
#include <print>
#include <ranges>
#include <vector>
int main() {
std::vector<int> data{1, 2, 3, 4, 5, 6, 7, 8};
for (auto chunk : data | std::views::chunk(3))
std::print("{}\n", chunk);
}
And the output:
[ 1 2 3 ]
[ 4 5 6 ]
[ 7 8 ]
views::chunk
splits the sequence into groups of three elements.
If the number of elements isn’t divisible by 3, the last chunk will contain fewer elements.
Example 2: Processing Network Packets in Chunks
chunk
can work not just with regular containers, but with just input ranges:
#include <print>
#include <ranges>
#include <sstream>
#include <vector>
int main() {
std::istringstream stream{"AB CD EF 12 34 56 78 95 FF"};
auto bytes = std::ranges::istream_view<std::string>(stream);
for (auto packet : bytes | std::views::chunk(4))
std::print("Packet: {}\n", packet);
}
Output:
Packet: ["AB", "CD", "EF", "12"]
Packet: ["34", "56", "78", "95"]
Packet: ["FF"]
This example simulates processing a byte stream in fixed 2-byte packets.
Dynamic Grouping with views::chunk_by
, C++26
When the group size isn’t fixed but defined by a condition, chunk_by
comes into play, which is a recent addition from C++26. This time, the range has to model forward range at least.
Example 1: Grouping by Parity (Even/Odd)
#include <print>
#include <ranges>
#include <vector>
int main() {
std::vector<int> values{1, 3, 5, 2, 4, 6, 7, 9, 8};
for (auto group : values | std::views::chunk_by([](int a, int b) {
return (a % 2) == (b % 2); // Same parity
})) {
std::print("size {}, {}\n", group.size(), group);
}
}
Output:
size 3, [1, 3, 5]
size 3, [2, 4, 6]
size 2, [7, 9]
size 1, [8]
This code dynamically groups consecutive numbers based on their parity. Each group contains either only odd or only even numbers.
Example 2: Extracting Sentences from Text
We can use chunk_by
and split at places where a sentence ends.
First try:
#include <print>
#include <ranges>
#include <string_view>
int main() {
using namespace std::string_view_literals;
constexpr auto text = "C++ is powerful. Ranges are elegant. This is fun!"sv;
for (auto sentence : text | std::views::chunk_by([](char a, char b) {
// Group until a dot is found; start a new group after '.'
return a != '.' && b != '.';
})) {
std::print("Sentence: {}\n", sentence);
}
}
And we get:
Sentence: ['C', '+', '+', ' ', 'i', 's', ' ', ...]
Sentence: ['.']
Sentence: [' ', 'R', 'a', 'n', 'g', 'e', 's', ...]
Sentence: ['.']
Sentence: [' ', 'T', 'h', 'i', 's', ' ', 'i', ...]
Probably not the best… but we can try cleaning the result:
int main() {
using namespace std::string_view_literals;
constexpr auto text = "C++ is powerful. Ranges are elegant. This is fun!"sv;
for (auto sentence : text | std::views::chunk_by([](char a, char b) {
// Group until a dot is found; start a new group after '.'
return a != '.' && b != '.';
})) {
// Remove leading spaces if any, and skip dots-only groups
auto view = std::string_view(&*sentence.begin(), std::ranges::distance(sentence));
view.remove_prefix(std::min(view.find_first_not_of(' '), view.size()));
if (!view.empty() && view != ".")
std::print("Sentence: [{}]\n", view);
}
}
And now we get:
Sentence: [C++ is powerful]
Sentence: [Ranges are elegant]
Sentence: [This is fun!]
Experiment @Compiler Explorer
Summary
Feature | split |
chunk |
chunk_by |
---|---|---|---|
C++ Standard | C++20 | C++23 | C++26 |
Fixed-size groups | ❌ | ✅ | ❌ |
Custom grouping | ❌ | ❌ | ✅ |
Text splitting | ✅ | ❌ | ❌ |
In this text, we explored various ways to split ranges using split
, chunk
or chunk_by
, we also touched a little bit about lazy_split
.
Back to you
- Which of these adaptors have you already tried?
- Which one surprised you the most?
Let me know your thoughts in the comments!
I've prepared a valuable bonus for you!
Learn all major features of recent C++ Standards on my Reference Cards!
Check it out here: