Adaptors

Flux adaptor functions take one or more sequences as input and return a new adapted sequence as output. When iterating, the elements of the adapted sequence are computed lazily from the elements of the underlying sequence. Adaptors are sink functions, meaning they take ownership of the sequences that are passed to them.

While functions below are shown as taking their sequence arguments by value for documentation purposes, in fact they will reject lvalue sequence arguments that are not trivially copyable. If you have a non-trivially copyable sequence then you need to explicitly copy or move it into the adaptor using std::move(), flux::copy() or (in C++23) auto(). This is to prevent accidental copying of, for example, large vectors.

You can pass a reference to a sequence into an adaptor using flux::ref() or flux::mut_ref(). Doing so introduces a data and lifetime dependency between the original sequence and the returned adaptor object. You must not modify the original sequence object (including calling its destructor) between the creation of the referencing adaptor object and its last use.

adjacent

template<distance_t N>
requires (N > 0)
auto adjacent(multipass_sequence auto seq) -> multipass_sequence auto

Given a compile-time size N and a multipass sequence seq, returns a new sequence which yields sliding windows of size N as an N-tuple of elements of seq. If seq has fewer than N elements, the adapted sequence will be empty.

The slide() adaptor is similar to adjacent(), but takes its window size as a run-time rather than a compile-time parameter, and returns length-n subsequences rather than tuples.

Equivalent to:

zip(seq, drop(seq, 1), drop(seq, 2), ..., drop(seq, N-1));
Template Parameters:

N – The size of the sliding window. Must be greater than zero.

Parameters:

seq – A multipass sequence.

Returns:

A sequence adaptor whose element type is a std::tuple of size N, or a std::pair if N == 2.

Models:

Concept

When

multipass_sequence

Always

bidirectional_sequence

seq is bidirectional

random_access_sequence

seq is random-access

contiguous_sequence

Never

bounded_sequence

seq is bidirectional and bounded

sized_sequence

seq is sized

infinite_sequence

Never

read_only_sequence

seq is read-only

const_iterable_sequence

seq is const-iterable

Example:

std::vector vec{1, 2, 3, 4, 5};

std::vector<int> sums;

// adjacent<3> yields 3-tuples which we can destructure
for (auto [a, b, c] : flux::adjacent<3>(std::move(vec))) {
    sums.push_back(a + b + c);
}

assert((sums == std::vector{1 + 2 + 3,
                            2 + 3 + 4,
                            3 + 4 + 5}));
See also:

adjacent_filter

template<multipass_sequence Seq, typename Pred>
requires std::predicate<Pred&, element_t<Seq>, element_t<Seq>>
auto adjacent_filter(Seq seq, Pred pred) -> multipass_sequence auto;

Applies the given binary predicate pred to each pair of adjacent elements of seq. If the predicate returns false, the second element of the pair does not appear in the resulting sequence. The first element of seq is always included in the output.

A common use for adjacent_filter() is to remove adjacent equal elements from a sequence, which can be achieved by passing std::not_equal_to{} as the predicate. The dedup() function is a handy alias for adjacent_filter(not_equal_to{}).

Parameters:
  • seq – A multipass sequence

  • pred – A binary predicate to compare sequence elements

Returns:

The filtered sequence

Models:

Example:

std::array nums{1, 1, 2, 3, 3, 2, 2};

// The adjacent_filter adaptor applies the given predicate to each pair
// of elements in the sequence, and if the predicate returns false then
// the second element of the pair is discarded
auto filtered1 = flux::adjacent_filter(nums, std::less{});
assert(flux::equal(filtered1, std::array{1, 2, 3}));

// For the common case of removing adjacent equal elements, Flux provides
// the dedup() function as shorthand for adjacent_filter(std::not_equal_to{})
auto filtered2 = flux::dedup(nums);
assert(flux::equal(filtered2, std::array{1, 2, 3, 2}));

// We can use adjacent_filter with a custom comparator as well
auto compare = [](auto p1, auto p2) { return p1.first != p2.first; };
std::pair<int, int> pairs[] = {{1, 2}, {1, 3}, {1, 4}, {2, 5}, {2, 6}};

auto filtered3 = flux::adjacent_filter(flux::ref(pairs), compare);
assert(flux::equal(filtered3,
                   std::array{std::pair{1, 2}, std::pair{2, 5}}));
See also:

adjacent_map

template<distance_t N>
auto adjacent_map(multipass_sequence auto seq, auto func) -> multipass_sequence auto;

Applies the N-ary function func to overlapping windows of size N.

Equivalent to map(adjacent<N>(seq), unpack(func)), but avoids forming an intermediate tuple.

Models:

Concept

When

multipass_sequence

Always

bidirectional_sequence

seq is bidirectional

random_access_sequence

seq is random-access

contiguous_sequence

Never

bounded_sequence

seq is bidirectional and bounded

sized_sequence

seq is sized

infinite_sequence

Never

read_only_sequence

seq is read-only

const_iterable_sequence

seq is const-iterable and func is const-invocable

cache_last

template<sequence Seq>
requires bounded_sequence<Seq> || (multipass_sequence<Seq> && !infinite_sequence<Seq>)
auto cache_last(Seq seq) -> sequence auto;

cache_last() is used to turn a non-bounded sequence into a bounded_sequence, by internally caching the cursor position of the last element,

If passed a sequence that is already bounded, it is returned unchanged. Otherwise, the passed-in sequence must be multipass, and not infinite.

Note that because this adaptor uses internal caching it is not const-iterable.

Parameters:

seq – A sequence we want to ensure is bounded.

Returns:

A bounded sequence

Models:

Concept

When

multipass_sequence

Seq is multipass

bidirectional_sequence

Seq is bidirectional

random_access_sequence

Seq is random-access

contiguous_sequence

Seq is contiguous (passthrough)

bounded_sequence

Always

sized_sequence

Seq is sized

infinite_sequence

Never

read_only_sequence

Seq is read-only

const_iterable_sequence

Seq is bounded and const-iterable (passthrough)

cartesian_power

template<distance_t N>
requires (N >= 0)
auto cartesian_power(multipass_sequence auto seq) -> multipass_sequence auto;

Returns a sequence object giving the N th Cartesian power of the elements of seq. It is equivalent to N nested for loops each iterating over seq.

The element type of the returned sequence is an N-tuple of the elements of seq. The number of elements is \(S^N\), where \(S\) is count(seq).

Equivalent to cartesian_product(seq, seq, ...), but stores only a single copy of seq.

Template Parameters:

N – The cartesian power

Parameters:

seq – A multipass sequence

Returns:

A multipass sequence yielding N-tuples of all combinations of elements of seq

Example:

std::string_view str = "ab";
std::vector<std::string> strings;

// cartesian_power<3> gives us 3-tuples which we can destructure
// The total number of elements in this case is size(str)^3 = 8
for (auto [x, y, z] : flux::cartesian_power<3>(str)) {
    strings.push_back(std::string{x, y, z});
}

assert((strings == std::vector<std::string>{"aaa", "aab", "aba", "abb",
                                            "baa", "bab", "bba", "bbb"}));
Models:

Concept

When

multipass_sequence

Always

bidirectional_sequence

seq is bidirectional

random_access_sequence

seq is random-access

contiguous_sequence

Never

bounded_sequence

seq is bounded

sized_sequence

seq is sized

infinite_sequence

Never

read_only_sequence

seq is read-only

const_iterable_sequence

seq is const-iterable

See also:

cartesian_power_map

template<distance_t N>
requires (N >= 0)
auto cartesian_power_map(multipass_sequence auto seq, auto func) -> multipass_sequence auto;

Returns a sequence adaptor which applies the N-ary function func to the N th Cartesian power of the elements of seq.

The total number of elements in the returned sequence is \(S^N\), where \(S\) is count(seq).

Equivalent to cartesian_power<N>(seq, unpack(func)), but avoids forming an intermediate tuple.

Template Parameters:

N – The cartesian power

Parameters:
  • seq – A multipass sequence

  • func – An N-ary function callable with the cartesian product of the elements of seq

Returns:

A multipass sequence whose elements are the result of applying func to N elements of seq

Example:

std::array nums{1, 2, 3};

// cartesian_power_map<N> takes a sequence and a callable of N arguments
// Here we are using N=2 and the binary function object std::plus
auto sums = flux::cartesian_power_map<2>(nums, std::plus{}).to<std::vector<int>>();

assert((sums == std::vector<int>{1 + 1, 1 + 2, 1 + 3,
                                 2 + 1, 2 + 2, 2 + 3,
                                 3 + 1, 3 + 2, 3 + 3}));
Models:

Concept

When

multipass_sequence

Always

bidirectional_sequence

seq is bidirectional

random_access_sequence

seq is random-access

contiguous_sequence

Never

bounded_sequence

seq is bounded

sized_sequence

seq is sized

infinite_sequence

Never

read_only_sequence

seq is read-only

const_iterable_sequence

seq is const-iterable and func is const-invocable

See also:

cartesian_product

auto cartesian_product(sequence auto seq0, multipass_sequence auto... seqs) -> sequence auto;

Returns an adaptor yielding the cartesian_product of the input sequences.

The element type of the returned sequence is a tuple of the element types of the input sequences.

Example:

std::string_view str = "abc";
std::array nums = {1, 2, 3};

// flux::cartesian_product(str, nums) yields all combinations of elements from
// the two sequences as a tuple<char, int>
auto pairs = flux::cartesian_product(str, nums).to<std::vector>();

using P = std::tuple<char, int>;

assert((pairs == std::vector{P{'a', 1}, P{'a', 2}, P{'a', 3},
                             P{'b', 1}, P{'b', 2}, P{'b', 3},
                             P{'c', 1}, P{'c', 2}, P{'c', 3}}));

// cartesian_product can take an arbitrary number of sequence arguments
// The number of elements is the product of the sizes of the input sequences
// It is bidirectional and random-access if all the input sequences
// satisfy these concepts
auto seq = flux::cartesian_product("xy"sv,
                                   std::array{1.0, 2.0},
                                   std::vector{111, 222}).reverse();

using T = std::tuple<char, double, int>;

assert(flux::equal(seq, std::vector<T>{{'y', 2.0, 222}, {'y', 2.0, 111},
                                       {'y', 1.0, 222}, {'y', 1.0, 111},
                                       {'x', 2.0, 222}, {'x', 2.0, 111},
                                       {'x', 1.0, 222}, {'x', 1.0, 111}}));
Models:

Concept

When

multipass_sequence

seq0 is multipass

bidirectional_sequence

All passed-in sequences are bidirectional and bounded

random_access_sequence

All passed-in sequences are random-access and bounded

contiguous_sequence

Never

bounded_sequence

seq0 is bounded

sized_sequence

All passed-in sequences are sized

infinite_sequence

Never

read_only_sequence

All passed-in sequences are read-only

const_iterable_sequence

All passed-in sequences are const-iterable

See also:

cartesian_product_map

template<typename Func, sequence Seq0, multipass_sequence... Seqs>
requires std::regular_invocable<Func&, element_t<Seq0>, element_t<Seqs>...>
auto cartesian_product_with(Func func, Seq0 seq0, Seqs... seqs) -> sequence auto;

Given N sequences and an N-ary function func, applies func to the cartesian product of the elements of the input sequences.

Equivalent to map(cartesian_product(seq0, seqs...), unpack(func)), but avoids forming an intermediate tuple.

Example:

std::array small = {1, 2};

auto add3 = [](long a, float b, int c) { return double(a) + b + c; };

// Note that because cartesian_product_map takes a variadic number of
// sequences, the function argument goes first
auto vec = flux::cartesian_product_map(add3, big, medium, small)
               .to<std::vector>();

assert((vec == std::vector<double>{10101.0, 10102.0,
                                   10201.0, 10202.0,
                                   20101.0, 20102.0,
                                   20201.0, 20202.0}));
Models:

Concept

When

multipass_sequence

seq0 is multipass

bidirectional_sequence

All passed-in sequences are bidirectional and bounded

random_access_sequence

All passed-in sequences are random-access and bounded

contiguous_sequence

Never

bounded_sequence

seq0 is bounded

sized_sequence

All passed-in sequences are sized

infinite_sequence

Never

read_only_sequence

All passed-in sequences are read-only

const_iterable_sequence

All passed-in sequences are const-iterable and func is const-invocable

See also:

chain

template<sequence Seq0, sequence... Seqs>
requires see_below
auto chain(Seq0 seq0, Seqs... seqs) -> sequence auto;

Combines the given input sequences into one logical sequence.

Requires:
  • The element types of the input sequences share a common reference type to which they are all convertible. This is the element type of the returned sequence.

  • The rvalue element types of the input sequences share a common reference type to which they are all convertible. This is the rvalue element type of the returned sequence.

  • The value types of the input sequences share a common type. This is the value type of the returned sequence.

Models:

Concept

When

multipass_sequence

All passed-in sequences are multipass

bidirectional_sequence

All passed-in sequences are bidirectional and bounded

random_access_sequence

All passed-in sequences are random-access and bounded

contiguous_sequence

Never

bounded_sequence

The last passed-in sequence is bounded

sized_sequence

All passed-in sequences are sized

infinite_sequence

Any passed-in sequence is infinite

read_only_sequence

All passed-in sequences are read-only

const_iterable_sequence

All passed-in sequences are const-iterable

chunk

auto chunk(sequence auto seq, std::integral auto chunk_sz) -> sequence auto;

Splits seq into a sequence of non-overlapping sequences, each of chunk_sz elements. The final sequence may have fewer elements.

Models:

Concept

When

multipass_sequence

seq is multipass

bidirectional_sequence

seq is bidirectional

random_access_sequence

seq is random-access

contiguous_sequence

Never

bounded_sequence

seq is multipass and bounded

sized_sequence

seq is sized

infinite_sequence

seq is infinite and multipass

read_only_sequence

seq is read-only

const_iterable_sequence

seq is const-iterable

chunk_by

template<multipass_sequence Seq, typename Pred>
requires std::predicate<Pred, element_t<Seq>, element_t<Seq>>
auto chunk_by(Seq seq, Pred pred) -> multipass_sequence auto;

Splits seq into a sequence of non-overlapping sequences using the binary predicate pred. The given predicate should return true if both elements should be considered part of the same chunk. If the predicate returns false, a new chunk will be started, with the second argument to pred belonging to the new chunk.

Models:

Concept

When

multipass_sequence

Always

bidirectional_sequence

Seq is bidirectional

random_access_sequence

Never

contiguous_sequence

Never

bounded_sequence

Seq is bounded

sized_sequence

Never

infinite_sequence

Never

read_only_sequence

Seq is read-only

const_iterable_sequence

Seq is const-iterable and Pred is const-invocable

cursors

auto cursors(multipass_sequence auto seq) -> multipass_sequence auto;

Given a sequence seq, cursors(seq) returns a new sequence whose elements are the cursors of the original sequence. The cursors() sequence retains all the capabilities of the source sequence (bidirectional, random access, sized etc), up to contiguous_sequence.

This is basically a passthrough adaptor, except that read_at(seq, cur) returns a copy of cur.

Parameters:

seq – A multipass sequence

Returns:

A sequence whose elements are the cursors of seq

Models:

Concept

When

multipass_sequence

Always

bidirectional_sequence

seq is bidirectional

random_access_sequence

seq is random-access

contiguous_sequence

Never

bounded_sequence

seq is bounded

sized_sequence

seq is sized

infinite_sequence

seq is infinite

read_only_sequence

Always

const_iterable_sequence

seq is const-iterable

Example:

std::array const array{"alpha"sv, "bravo"sv, "charlie"sv, "delta"sv, "echo"sv};

auto long_words = flux::drop(array, 2);

// We can use the cursors() adaptor to iterate over the cursors of the
// sequence (in this case integer indices) and use those to read from the
// original sequence
for (auto idx : flux::cursors(long_words)) {
    std::cout << idx << ": " << long_words[idx] << '\n';
}
// prints
// 2: charlie
// 3: delta
// 4: echo

cycle

template<sequence Seq>
requires multipass_sequence<Seq> || infinite_sequence<Seq>
auto cycle(Seq seq) -> infinite_sequence auto;
template<multipass_sequence Seq>
auto cycle(Seq seq, std::integral auto count) -> multipass_sequence auto;

Repeats the elements of seq endlessly (for the first overload) or count times (for the second overload).

For the first overload, if Seq is already an infinite_sequence, it is passed through unchanged.

Otherwise, both overloads require a multipass_sequence, and the output is always a multipass_sequence. The adapted sequence is also a bidirectional_sequence when Seq is both bidirectional and bounded, and a random_access_sequence when Seq is random-access and bounded.

For the second overload, the returned sequence is additionally always a bounded_sequence (even if Seq is not), and a sized_sequence when the source sequence is sized.

To avoid “spooky action at a distance” (where mutating s[n] would change the value of some other s[m]) cycle() provides only immutable access to the elements of seq: that is, it behaves as if seq were first passed through read_only().

Caution

In order to provide random-access functionality, cursors for cycled sequences keep a size_t count of how many times they have looped round. For very long-running programs using the infinite version of cycle() it may be possible to overflow this counter. (Assuming 1000 iterations per second, this would take approximately 49 days on a machine with a 32-bit size_t, or around 500 million years on a 64-bit machine.)

While this won’t cause undefined behaviour, it’s possible to encounter incorrect results or runtime errors when using the random-access functions on cursors which have overflowed.

Parameters:
  • seq – A sequence to cycle through

  • count – The number of times to loop through the sequence before terminating. If not supplied, the sequence will be repeated endlessly.

Returns:

An adapted sequence which repeatedly loops through the elements of seq.

Models:

Concept

When

multipass_sequence

Seq is multipass

bidirectional_sequence

Seq is bidirectional and bounded

random_access_sequence

Seq is random-access and bounded

contiguous_sequence

Never

bounded_sequence

If count is supplied

sized_sequence

If count is supplied

infinite_sequence

If count is not supplied

read_only_sequence

Always

const_iterable_sequence

Seq is const-iterable

Example:

std::array arr{1, 2, 3};

// cycle(seq) returns an infinite sequence. It's common to use this in
// combination with take() to turn it back into a finite sequence:
auto cycled1 = flux::take(flux::cycle(arr), 5);
assert(flux::equal(cycled1, std::array{1, 2, 3, 1, 2}));

// We can also use a cycled sequence as an argument to zip():
std::string_view letters = "ABCDE";
auto zipped = flux::zip(letters, flux::cycle(arr));
using P = std::pair<char const&, int const&>;
assert(flux::equal(zipped, std::array{
    P{'A', 1}, P{'B', 2}, P{'C', 3}, P{'D', 1}, P{'E', 2}
}));

// Alternatively, we can provide a second argument to cycle(seq, n) to
// get a finite sequence which repeats the source n times:
auto cycled2 = flux::cycle(arr, 3);
assert(flux::equal(cycled2, std::array{1, 2, 3, 1, 2, 3, 1, 2, 3}));
assert(flux::sum(cycled2) == 18);

// Note that both versions of cycle() only provide immutable access to their
// elements. The following would be a compile error:
// flux::fill(cycled2, 99); // ERROR: cannot assign to const reference
See also:

dedup

template<multipass_sequence Seq>
requires std::equality_comparable<element_t<Seq>>
auto dedup(Seq seq) -> multipass_sequence auto;

An alias for adjacent_filter(seq, std::ranges::not_equal_to{}). This can be used to remove adjacent elements from a sequence.

See also:

drop

auto drop(sequence auto seq, std::integral auto count) -> sequence auto;

Given a sequence seq and a non-negative integral value count, returns a new sequence which skips the first count elements of seq.

The returned sequence has the same capabilities as seq. If seq has fewer than count elements, the returned sequence is empty.

Parameters:
  • seq – A sequence.

  • count – A non-negative integral value indicating the number of elements to be skipped.

Returns:

A sequence adaptor that yields the remaining elements of seq, with the first count elements skipped.

Models:

Concept

When

multipass_sequence

seq is multipass

bidirectional_sequence

seq is bidirectional

random_access_sequence

seq is random-access

contiguous_sequence

seq is contiguous

bounded_sequence

seq is bounded

sized_sequence

seq is sized

infinite_sequence

seq is infinite

read_only_sequence

seq is read-only

const_iterable_sequence

seq is const-iterable

Example:

std::vector vec{1, 2, 3, 4, 5};

auto dropped = flux::drop(std::move(vec), 3);

assert(flux::size(dropped) == 2);
assert(flux::equal(dropped, std::vector{4, 5}));
See also:

drop_while

template<sequence Seq, typename Pred>
requires std::predicate<Pred&, element_t<Seq>>
auto drop_while(Seq seq, Pred pred) -> sequence auto;

Skips elements from the from of seq while pred returns true. Once pred has returned false, the adaptor has done its job and the remaining elements of seq are returned as normal.

Models:

Concept

When

multipass_sequence

Seq is multipass

bidirectional_sequence

Seq is bidirectional

random_access_sequence

Seq is random-access

contiguous_sequence

Seq is contiguous

bounded_sequence

Seq is bounded

sized_sequence

Seq is sized

infinite_sequence

Seq is infinite

read_only_sequence

Seq is read-only

const_iterable_sequence

Seq is const-iterable and Pred is const-invocable

filter

template<sequence Seq, typename Pred>
requires std::predicate<Pred, element_t<Seq>&>
auto filter(Seq seq, Pred pred) -> sequence auto;

Skips elements of seq for which unary predicate pred returns false.

Models:

Concept

When

multipass_sequence

Seq is multipass

bidirectional_sequence

Seq is bidirectional

random_access_sequence

Never

contiguous_sequence

Never

bounded_sequence

Seq is bounded

sized_sequence

Never

infinite_sequence

Seq is infinite

read_only_sequence

Seq is read-only

const_iterable_sequence

Seq is const-iterable and Pred is const-invocable

flatten

template<sequence Seq>
requires sequence<element_t<Seq>>
auto flatten(Seq seq) -> sequence auto;

Given a sequence-of-sequences, removes one level of nesting.

Models:

Concept

When

multipass_sequence

Seq is multipass, element_t<Seq> is multipass and element_t<Seq> is a reference type

bidirectional_sequence

Seq is bidirectional, element_t<Seq> is bidirectional and element_t<Seq> is a reference type

random_access_sequence

Never

contiguous_sequence

Never

bounded_sequence

Seq is bounded

sized_sequence

Never

infinite_sequence

Never

read_only_sequence

Seq is read-only

const_iterable_sequence

Seq and element_t<Seq> are both const-iterable multipass sequences, and element_t<Seq> is a reference type

map

template<sequence Seq, typename Func>
requires std::regular_invocable<Func&, element_t<Seq>>
auto map(Seq seq, Func func) -> sequence auto;

Turns a sequence-of-T into a sequence-of-U, where U is the return type of func.

Models:

Concept

When

multipass_sequence

Seq is multipass

bidirectional_sequence

Seq is bidirectional

random_access_sequence

Seq is random-access

contiguous_sequence

Never

bounded_sequence

Seq is bounded

sized_sequence

Seq is sized

infinite_sequence

Seq is infinite

read_only_sequence

The return type of func is a prvalue or a const reference

const_iterable_sequence

Seq is const-iterable and func is const-invocable

mask

template<sequence Seq, sequence Mask>
requires boolean_testable<element_t<Mask>>
auto mask(Seq seq, Mask where) -> sequence auto;

Given a sequence of values and a sequence of booleans, mask() yields those elements of seq for which the corresponding element of where evaluates to true. Iteration is complete when either of the two input sequences is exhausted.

The returned sequence models the lowest common category of the two input sequences, up to bidirectional_sequence. It is also a bounded_sequence and a sized_sequence when both inputs model these concepts.

Parameters:
  • seq – A sequence of values

  • where – A sequence whose element type is convertible to bool

Returns:

An adapted sequence whose elements are the elements of seq, filtered by the corresponding elements of where

Models:

Concept

When

multipass_sequence

Seq and Mask are both multipass

bidirectional_sequence

Seq and Mask are both bidirectional

random_access_sequence

Never

contiguous_sequence

Never

bounded_sequence

Seq and Mask are both bounded

sized_sequence

Seq and Mask are both sized

infinite_sequence

Seq and Mask are both infinite

read_only_sequence

Seq is read-only

const_iterable_sequence

Seq and Mask are both const-iterable

Example:

std::array values{"one"sv, "two"sv, "three"sv, "four"sv, "five"sv};

std::array selectors{true, false, true, false, true};

// flux::mask() selects those elements of values for which the corresponding
// element of selectors is true
auto masked = flux::mask(values, selectors);
assert(flux::equal(masked, std::array{"one"sv, "three"sv, "five"sv}));

// Note that the selectors sequence can have any element type which is
// explicitly convertible to bool
std::array int_selectors{0, 0, 0, 1, 1};

auto masked2 = flux::mask(values, int_selectors);
assert(flux::equal(masked2, std::array{"four"sv, "five"sv}));
See also:

pairwise

auto pairwise(multipass_sequence auto seq) -> multipass_sequence auto;

Returns an adaptor which yields pairs of elements of seq. It is an alias for adjacent<2>().

Parameters:

seq – A multipass sequence.

Returns:

A multipass sequence yielding pairs of elements of seq.

pairwise_map

template<multipass_sequence Seq, typename Func>
requires std::regular_invocable<Func&, element_t<Seq>, element_t<Seq>> && can_reference<std::invoke_result_t<Func&, element_t<Seq>, element_t<Seq>>>
auto pairwise_map(Seq seq, Func func) -> multipass_sequence auto;

An alias for adjacent_map<2>.

prescan

template<sequence Seq, typename Func, std::movable Init>
requires foldable<Seq, Func, Init>
auto prescan(Seq seq, Func func, Init init) -> sequence auto;

Returns a stateful sequence adaptor which yields “partial folds” using the binary function func.

First, this adaptor initialises an internal variable state to init and yields a read-only reference to this state. Then, for each successive element elem of the underlying sequence, it sets:

state = func(std::move(state), std::forward(elem));

and yields a read-only reference to the new state.

The final value yielded by this adaptor is the same as fold(seq, func, init).

Because this adaptor needs to maintain internal state, it is only ever single-pass. However it is a bounded_sequence when the underlying sequence is bounded and a sized_sequence when the underlying sequence is sized.

Unlike scan(), this function performs an exclusive scan, that is, the Nth element of the adapted sequence does not include the Nth element of the underlying sequence. The adaptor returned by prescan() always yields at least one element – the initial value – followed by the elements that would be yielded by the scan() adaptor.

Parameters:
  • seq – A sequence to adapt

  • func – A binary callable of the form R(R, element_t<Seq>), where R is constructible from Init

  • init – The initial value for the scan

Returns:

A sequence adaptor which performs an exclusive scan of the elements of seq using func.

Models:

Example:

// We can compute the triangular numbers using prescan()
std::array const ints{1, 2, 3, 4, 5};

// Note that unlike scan(), the initial value for prescan() is required, and
// is the first element of the resulting sequence, which has one more element
// than the input
auto tri_nums = flux::prescan(ints, std::plus{}, 0);
assert(flux::equal(tri_nums, std::array{0, 1, 3, 6, 10, 15}));
See also:

read_only

template<sequence Seq>
auto read_only(Seq seq) -> read_only_sequence auto;

Returns an adapted sequence which prevents direct modification of the elements of seq. The returned sequence retains the capabilities of the source sequence, all the way up to contiguous_sequence.

If Seq is already a read_only_sequence, then it is returned unchanged. Otherwise, read_only() is equivalent to:

map(seq, [](auto&& elem) -> const_element_t<Seq> {
    return static_cast<const_element_t<Seq>>(std::forward(elem));
});

except that the returned sequence will be a contiguous_sequence if the source sequence models that concept. In this case, the pointer returned from data() will have type value_t<Seq> const*.

Parameters:

seq – A sequence

Returns:

An adapted sequence which provides read-only access to the elements of seq

Models:

Concept

When

multipass_sequence

Seq is multipass

bidirectional_sequence

Seq is bidirectional

random_access_sequence

Seq is random-access

contiguous_sequence

Seq is contiguous

bounded_sequence

Seq is bounded

sized_sequence

Seq is sized

infinite_sequence

Seq is infinite

read_only_sequence

Always

const_iterable_sequence

Seq is const-iterable

Example:

// We can use the read_only_sequence concept to statically require a sequence
// whose elements are immutable
bool contains_a_two(flux::read_only_sequence auto&& seq)
{
    for (auto&& elem : seq) {
        if (elem == 2) { // What if we wrote `elem = 2` (assignment) by mistake?
            return true;
        }
    }
    return false;
}

int main()
{
    auto seq = flux::filter(std::vector{1, 2, 3, 4, 5}, flux::pred::even);

    // We cannot pass seq directly, as it yields mutable int& elements
    // contains_a_two(seq); // COMPILE ERROR

    // ...but we can use read_only() so that the sequence yields immutable
    // elements of type int const&.
    assert(contains_a_two(flux::read_only(std::move(seq))));
}
See also:

reverse

template<bidirectional_sequence Seq>
requires bounded_sequence<Seq>
auto reverse(Seq seq) -> bidirectional_sequence auto;

Given a bounded, bidirectional sequence seq, returns an adaptor which yields the elements of seq in reverse order.

Models:

scan

template<sequence Seq, typename Func, std::movable Init = value_t<Seq>>
requires foldable<Seq, Func, Init>
auto scan(Seq seq, Func func, Init init = {}) -> sequence auto;

Returns a stateful sequence adaptor which yields “partial folds” using the binary function func.

First, this adaptor initialises an internal variable state to init. Then, for each successive element elem of the underlying sequence, it sets:

state = func(std::move(state), std::forward(elem));

and yields a read-only reference to the new state.

The final value yielded by this adaptor is the same as fold(seq, func, init).

Because this adaptor needs to maintain internal state, it is only ever single-pass. However it is a bounded_sequence when the underlying sequence is bounded and a sized_sequence when the underlying sequence is sized.

Unlike prescan(), this function performs an inclusive scan, that is, the Nth element of the adapted sequence includes the Nth element of the underlying sequence. The adapted sequence always yields the same number of elements as the underlying sequence.

Parameters:
  • seq – A sequence to adapt

  • func – A binary callable of the form R(R, element_t<Seq>), where R is constructible from Init

  • init – The initial value for the scan. If not supplied, a default constructed object of type value_t<Seq> is used.

Returns:

A sequence adaptor which performs an inclusive scan of the elements of seq using func.

Models:

Example:

// We can compute the triangular numbers using scan()
std::array const ints{1, 2, 3, 4, 5};

// Note that unlike prescan(), the initial value for scan() may be omitted
// (here defaulting to int{}), and the resulting sequence has the same number
// of elements as the original
auto tri_nums = flux::scan(ints, std::plus{});
assert(flux::equal(tri_nums, std::array{1, 3, 6, 10, 15}));
See also:

scan_first

template<sequence Seq, typename Func>
requires foldable<Seq, Func, element_t<Seq>>
auto scan_first(Seq seq, Func func) -> sequence auto;

Returns a stateful sequence adaptor which yields “partial folds” using the binary function func.

When iterated over, the returned sequence first initialises an internal variable state with the first element of the underlying sequence, and yields a read-only reference to this state. For each subsequent element elem, it sets:

state = func(std::move(state), std::forward(elem));

and yields a read-only reference to the internal state. If seq is empty, the internal state is never initialised and the resulting sequence is also empty. For a non-empty sequence, the final value yielded by scan_first() is the same as would be obtained from fold_first(seq, func).

Because this adaptor needs to maintain internal state, it is only ever single-pass. However it is a bounded_sequence when the underlying sequence is bounded and a sized_sequence when the underlying sequence is sized.

Like scan(), this function performs an inclusive scan, that is, the Nth element of the adapted sequence includes the Nth element of the underlying sequence. The adapted sequence always yields the same number of elements as the underlying sequence. Unlike scan(), the first element of scan_first() is simply the first element of the underlying sequence, and the supplied func is only applied to subsequent elements (this is equivalent to the differing behaviours of fold() and fold_first() respectively).

Parameters:
  • seq – A sequence to adapt

  • func – A binary callable of the form R(R, element_t<Seq>), where R is constructible from element_t<Seq>

Returns:

A sequence adaptor which performs an inclusive scan of the elements of seq using func.

Models:

Example:

// We can compute the triangular numbers using scan_first()
std::array const ints{1, 2, 3, 4, 5};

// Note that unlike scan(), scan_first() never takes an initial value,
// and instead the first element of the resulting sequence is the same as
// the first element of the underlying sequence: the fold operation is only
// applied to subsequent elements.
auto tri_nums = flux::scan_first(ints, std::plus{});
assert(flux::equal(tri_nums, std::array{1, 3, 6, 10, 15}));
See also:

set_difference

template<sequence Seq1, sequence Seq2, typename Cmp = std::ranges::less>
requires strict_weak_order_for<Cmp, Seq1> && strict_weak_order_for<Cmp, Seq2>
auto set_difference(Seq1 seq1, Seq2 seq2, Cmp cmp = {}) -> sequence auto;

Returns a sequence adaptor which yields the set difference of the two input sequences seq1 and seq2, ordered by the given comparison function cmp.

This function assumes that both seq1 and seq2 are sorted with respect to the comparison function cmp. If the input sequences are not sorted, the contents of the resulting sequence is unspecified.

When the resulting sequence is iterated, it will output the elements from seq1 which are not found in the seq2 according to cmp. If some element is found m times in seq1 and n times in seq2, then the resulting sequence yields exactly std::max(m - n, 0) elements.

Parameters:
  • seq1 – The first sorted sequence.

  • seq2 – The second sorted sequence.

  • cmp – A binary predicate that takes two elements as arguments and returns true if the first element is less than the second.

Returns:

A sequence adaptor that yields those elements of seq1 which do not also appear in seq2.

Models:

Concept

When

multipass_sequence

Seq1 and Seq2 are both multipass

bidirectional_sequence

Never

random_access_sequence

Never

contiguous_sequence

Never

bounded_sequence

Never

sized_sequence

Never

infinite_sequence

Never

read_only_sequence

Seq1 is read-only

const_iterable_sequence

Seq1 and Seq2 are both const-iterable, and cmp is const-invocable

Example:

std::array arr1{0, 1, 2, 3, 4, 5};
std::array arr2{   1,    3,    5};

auto merged = flux::set_difference(flux::ref(arr1), flux::ref(arr2));

assert(flux::equal(merged, std::array{0, 2, 4}));
See also:

set_intersection

template<sequence Seq1, sequence Seq2, typename Cmp = std::ranges::less>
requires strict_weak_order_for<Cmp, Seq1> && strict_weak_order_for<Cmp, Seq2>
auto set_intersection(Seq1 seq1, Seq2 seq2, Cmp cmp = {}) -> sequence auto;

Returns a sequence adaptor which yields the set intersection of the two input sequences seq1 and seq2, ordered by the given comparison function cmp.

This function assumes that both seq1 and seq2 are sorted with respect to the comparison function cmp. If the input sequences are not sorted, the contents of the resulting sequence is unspecified.

When the resulting sequence is iterated, it will output the elements from seq1 that are found in both sorted sequences according to cmp. If some element is found m times in seq1 and n times in seq2, then the resulting sequence yields exactly std::min(n, m) elements.

Parameters:
  • seq1 – The first sorted sequence.

  • seq2 – The second sorted sequence.

  • cmp – A binary predicate that takes two elements as arguments and returns true if the first element is less than the second.

Returns:

A sequence adaptor that represents the set intersection of the two input sequences.

Models:

Concept

When

multipass_sequence

Seq1 and Seq2 are both multipass

bidirectional_sequence

Never

random_access_sequence

Never

contiguous_sequence

Never

bounded_sequence

Never

sized_sequence

Never

infinite_sequence

Never

read_only_sequence

Seq1 is read-only

const_iterable_sequence

Seq1 and Seq2 are both const-iterable, and cmp is const-invocable

Example:

std::array arr1{0, 1, 2, 3, 4, 5};
std::array arr2{   1,    3,    5};

auto merged = flux::set_intersection(flux::ref(arr1), flux::ref(arr2));

assert(flux::equal(merged, std::array{1, 3, 5}));
See also:

set_symmetric_difference

template<sequence Seq1, sequence Seq2, typename Cmp = std::ranges::less>
requires see_below
auto set_symmetric_difference(Seq1 seq1, Seq2 seq2, Cmp cmp = {}) -> sequence auto;

Returns a sequence adaptor which yields the set symmetric difference of the two input sequences seq1 and seq2, ordered by the given comparison function cmp.

This function assumes that both seq1 and seq2 are sorted with respect to the comparison function cmp. If the input sequences are not sorted, the contents of the resulting sequence is unspecified.

When the resulting sequence is iterated, it will output the elements that are found in either of the sequence, but not in both of them according to cmp. If some element is found m times in seq1 and n times in seq2, then the resulting sequence yields exactly std::abs(m - n) elements, preserving order:

  • if m > n, the final m - n of these elements from seq1

  • if m < n, the final n - m of these elements from seq2

Requires:

The expression in the requires clause is equivalent to:

std::common_reference_with<element_t<Seq1>, element_t<Seq2>> &&
std::common_reference_with<rvalue_element_t<Seq1>, rvalue_element_t<Seq2>> &&
requires { typename std::common_type_t<value_t<Seq1>, value_t<Seq2>>; } &&
strict_weak_order_for<Cmp, Seq1> &&
strict_weak_order_for<Cmp, Seq2>
Parameters:
  • seq1 – The first sequence to merge.

  • seq2 – The second sequence to merge.

Returns:

A sequence adaptor that yields elements of seq1 and seq2 which do not appear in both sequences.

Models:

Concept

When

multipass_sequence

Seq1 and Seq2 are both multipass

bidirectional_sequence

Never

random_access_sequence

Never

contiguous_sequence

Never

bounded_sequence

Seq1 and Seq2 are both bounded

sized_sequence

Never

infinite_sequence

Never

read_only_sequence

std::common_reference_t<element_t<Seq1>, element_t<Seq2>> is an object type or a const reference

const_iterable_sequence

Seq1 and Seq2 are both const-iterable, and cmp is const-invocable

Example:

std::array arr1{0, 1, 2, 3, 4, 5};
std::array arr2{   1,    3,    5, 6, 7};

auto merged = flux::set_symmetric_difference(flux::ref(arr1), flux::ref(arr2));

assert(flux::equal(merged, std::array{0, 2, 4, 6, 7}));
See also:

set_union

template<sequence Seq1, sequence Seq2, typename Cmp = std::ranges::less>
requires see_below
auto set_union(Seq1 seq1, Seq2 seq2, Cmp cmp = {}) -> sequence auto;

Returns a sequence adaptor which yields the set union of the two input sequences seq1 and seq2, ordered by the given comparison function cmp.

This function assumes that both seq1 and seq2 are sorted with respect to the comparison function cmp. If the input sequences are not sorted, the contents of the resulting sequence is unspecified.

When the resulting sequence is iterated, it will output the elements from the two input sequences in order according to cmp. If some element is found m times in seq1 and n times in seq2, then the resulting sequence yields exactly std::max(n, m) elements.

Requires:

The expression in the requires clause is equivalent to:

std::common_reference_with<element_t<Seq1>, element_t<Seq2>> &&
std::common_reference_with<rvalue_element_t<Seq1>, rvalue_element_t<Seq2>> &&
requires { typename std::common_type_t<value_t<Seq1>, value_t<Seq2>>; } &&
strict_weak_order_for<Cmp, Seq1> &&
strict_weak_order_for<Cmp, Seq2>
Parameters:
  • seq1 – The first sorted sequence to merge.

  • seq2 – The second sorted sequence to merge.

  • cmp – A binary predicate that takes two elements as arguments and returns true if the first element is less than the second.

Returns:

A sequence adaptor that represents the set union of the two input sequences.

Models:

Concept

When

multipass_sequence

Seq1 and Seq2 are both multipass

bidirectional_sequence

Never

random_access_sequence

Never

contiguous_sequence

Never

bounded_sequence

Seq1 and Seq2 are both bounded

sized_sequence

Never

infinite_sequence

Never

read_only_sequence

std::common_reference_t<element_t<Seq1>, element_t<Seq2>> is an object type or a const reference

const_iterable_sequence

Seq1 and Seq2 are both const-iterable, and cmp is const-invocable

Example:

std::array arr1{1, 3, 5};
std::array arr2{1, 2, 4, 6};

auto merged = flux::set_union(flux::ref(arr1), flux::ref(arr2));

assert(flux::equal(merged, std::array{1, 2, 3, 4, 5, 6}));
See also:

slide

auto slide(multipass_sequence auto seq, std::integral auto win_sz) -> multipass_sequence auto;

Returns an adaptor which yields length-win_sz overlapping subsequences of seq.

The adjacent() adaptor is similar to slide(), but takes its window size argument as a compile-time rather than a run-time parameter, and yield N-tuples rather than subsequences.

Models:

Concept

When

multipass_sequence

Always

bidirectional_sequence

seq is bidirectional

random_access_sequence

seq is random-access

contiguous_sequence

Never

bounded_sequence

seq is bidirectional and bounded

sized_sequence

seq is sized

infinite_sequence

Never

read_only_sequence

seq is read-only

const_iterable_sequence

seq is const-iterable

split

template<multipass_sequence Seq, typename Delim>
requires std::equality_comparable_with<element_t<Seq>, Delim const&>
auto split(Seq seq, Delim delim) -> multipass_sequence auto;
template<multipass_sequence Seq, multipass_sequence Pattern>
requires std::equality_comparable_with<element_t<Seq>, element_t<Pattern>>
auto split(Seq seq, Pattern pattern) -> multipass_sequence auto;
template<multipass_sequence Seq, typename Pred>
requires std::predicate<Pred const&, element_t<seq>>
auto split(Seq seq, Pred pred) -> multipass_sequence auto;

Splits a multipass_sequence into a sequence-of-subsequences using the given argument.

The first overload takes a delimiter, which must be equality comparable with the source sequence’s value type. The source sequence will be split on each occurrence of the delimiter, with the delimiter itself removed. Consecutive delimiters will result in empty subsequences in the output. If the source sequence begins with a delimiter then the first subsequence will be empty, and likewise if it ends with a delimiter then the final subsequence will be empty.

The second overload takes another sequence, the pattern, whose elements must be equality comparable with the elements of the source sequence. The source is split whenever the pattern occurs as a subsequence. Consecutive (non-overlapping) occurrences of the pattern will result in empty sequences in the output. If ends_with(seq, pattern) is true, the final subsequence will be empty.

The third overload takes a unary predicate which will be called with successive elements of the source sequence and returns true when a split should occur. The “true” element will be removed from the output. If the predicate returns true for two consecutive of the source, then the output will contain an empty subsequence. If the predicate returns true` for the final element of the source, then the final subsequence will be empty.

The returned sequence is always a multipass_sequence. It is additionally a bounded_sequence when Seq is bounded.

Parameters:
  • seq – A multipass sequence to split.

  • delim – For the first overload, a delimiter to split on. Must be equality comparable with the element type of seq

  • pattern – For the second overload, a multipass sequence to split on. Its element type must be equality comparable with the element type of seq.

  • pred – For the third overload, a unary predicate accepting elements of seq, returning true when a split should occur.

Returns:

A multipass sequence whose elements are subsequences of seq.

Models:

Concept

When

multipass_sequence

Always

bidirectional_sequence

Never

random_access_sequence

Never

contiguous_sequence

Never

bounded_sequence

Seq is bounded

sized_sequence

Never

infinite_sequence

Never

read_only_sequence

Seq is read-only

const_iterable_sequence

Seq is const-iterable, and Pattern is const-iterable (for the second overload) or Pred is const-invocable (for the third overload)

Example:

using flux::equal;

// We can split a sequence using a single delimiter
auto seq1 = flux::split("here are some words"sv, ' ');
assert(equal(seq1, std::array{"here"sv, "are"sv, "some"sv, "words"sv}));


// Consecutive delimiters will result in empty subsequences in the output
auto seq2 = flux::split("some,,,commas"sv, ',');
assert(equal(seq2, std::array{"some"sv, ""sv, ""sv, "commas"sv}));


// If the sequence ends with a delimiter, the final subsequence will be empty
auto seq3 = flux::split("Two. Sentences."sv, '.');
assert(equal(seq3, std::array{"Two"sv, " Sentences"sv, ""sv}));


// We can also split a sequence with a pattern
auto seq4 = flux::split(std::vector{1, 2, 3, 4, 5}, std::array{2, 3});
assert(equal(seq4, std::vector{std::vector{1}, std::vector{4, 5}}));


// Repeated, non-overlapping patterns result in empty subsequences
auto seq5 = flux::split("Hello!!!!World"sv, "!!"sv);
assert(equal(seq5, std::array{"Hello"sv, ""sv, "World"sv}));


// Overlapping patterns are only matched once
auto seq6 = flux::split("Hello!!!World"sv, "!!"sv);
assert(equal(seq6, std::array{"Hello"sv, "!World"sv}));


// If the sequence begins with the pattern, the first subsequence will
// be empty...
auto seq7 = flux::split("!!Hello"sv, "!!"sv);
assert(equal(seq7, std::array{""sv, "Hello"sv}));


// ... and likewise if it ends with the pattern
auto seq8 = flux::split("Hello!!"sv, "!!"sv);
assert(equal(seq8, std::array{"Hello"sv, ""sv}));


// Lastly, we can split using a predicate function
auto is_digit = [](char c) { return c >= '0' && c <= '9'; };

auto seq9 = flux::split("These1are2some3words"sv, is_digit);
assert(equal(seq9, std::array{"These"sv, "are"sv, "some"sv, "words"sv}));


// As usual, consecutive "true" elements in the input will produce
// empty subsequences in the output
auto seq10 = flux::split("A123B"sv, is_digit);
assert(equal(seq10, std::array{"A"sv, ""sv, ""sv, "B"sv}));


// It can be useful combine splitting with a "not empty" filter
auto is_space = [](char c) { return std::isspace(static_cast<unsigned char>(c)); };

auto seq11 = flux::split("Alpha  Bravo\t\rCharlie \n"sv, is_space)
                    .filter(std::not_fn(flux::is_empty));
assert(equal(seq11, std::array{"Alpha"sv, "Bravo"sv, "Charlie"sv}));
See also:

stride

auto stride(sequence auto seq, std::integral auto stride_len) -> sequence auto;

Returns an adapted sequence which yields the same first element as seq and then yields every stride_len-th element after that.

Models:

Concept

When

multipass_sequence

seq is multipass

bidirectional_sequence

seq is bidirectional

random_access_sequence

seq is random-access

contiguous_sequence

Never

bounded_sequence

seq is bidirectional, bounded and sized

sized_sequence

seq is sized

infinite_sequence

seq is infinite

read_only_sequence

seq is read-only

const_iterable_sequence

seq is const-iterable

take

auto take(sequence auto seq, std::integral auto count) -> sequence auto;

Returns an adapted sequence with at most count elements.

Models:

Concept

When

multipass_sequence

seq is multipass

bidirectional_sequence

seq is bidirectional

random_access_sequence

seq is random-access

contiguous_sequence

seq is contiguous

bounded_sequence

seq is either infinite or both random-access and sized

sized_sequence

seq is sized or infinite

infinite_sequence

seq is infinite

read_only_sequence

seq is read-only

const_iterable_sequence

seq is const-iterable

take_while

template<sequence Seq, typename Pred>
requires std::predicate<Pred&, element_t<Seq>>
auto take_while(Seq seq, Pred pred) -> sequence auto;

Returns an adapted sequence which yields elements of seq while pred returns true. Iteration is complete when pred returns false or the underlying sequence is exhausted.

Models:

unchecked

auto unchecked(sequence auto seq) -> sequence auto;

A passthrough adaptor which disables bounds checking.

It does so by forwarding each call to read_at() on the adapted sequence to the read_at_unchecked() implementation of the underlying sequence.

Danger

Using this adaptor can result in undefined behaviour that would otherwise be caught by Flux’s safety checks. Use it with great caution.

Models:

Concept

When

multipass_sequence

seq is multipass

bidirectional_sequence

seq is bidirectional

random_access_sequence

seq is random-access

contiguous_sequence

seq is contiguous

bounded_sequence

seq is bounded

sized_sequence

seq is sized

infinite_sequence

seq is infinite

read_only_sequence

seq is read-only

const_iterable_sequence

seq is const-iterable

zip

auto zip(sequence auto... seqs) -> sequence auto;

Returns a sequence adaptor which iterates over the input sequences in lock-step. Iteration is complete when any of the input sequences is exhausted.

The element type of the returned sequence is a std::tuple of the element types of the input sequences, or a std::pair for two inputs.

Models:

Concept

When

multipass_sequence

All inputs are multipass

bidirectional_sequence

All inputs are bidirectional

random_access_sequence

seq is random-access

contiguous_sequence

Never

bounded_sequence

All inputs are bounded

sized_sequence

All inputs are sized

infinite_sequence

All inputs are infinite

read_only_sequence

All inputs are read-only

const_iterable_sequence

All inputs are const-iterable