std::ranges::next(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

✨ std::ranges::next(3) β€” C++ Standard Libary ✨

πŸ’– NAME πŸ’–

std::ranges::next β€” std::ranges::next, bestie. That’s literally the name, and it’s iconic.


πŸ¦‹ SYNOPSIS πŸ¦‹

Okay so first things first: this lives in the header <iterator>, and the call signatures are giving very much β€œfour flavors of the same cutie.” Here they are, exactly:

Defined in header <iterator>
Call signature
template< std::input_or_output_iterator I >
(1) (since C++20)
constexpr I next( I i );
template< std::input_or_output_iterator I >
(2) (since C++20)
constexpr I next( I i, std::iter_difference_t<I> n );
template< std::input_or_output_iterator I, std::sentinel_for<I> S
>
(3) (since C++20)
constexpr I next( I i, S bound );
template< std::input_or_output_iterator I, std::sentinel_for<I> S
>
(4) (since C++20)
constexpr I next( I i, std::iter_difference_t<I> n, S bound );

The whole vibe: return the n^th successor of iterator i. Slay.

The function-like entities described on this page are niebloids, that is:

  • Explicit template argument lists cannot be specified when calling any of them.
  • None of them are visible to argument-dependent lookup.
  • When any of them are found by normal unqualified lookup as the name to the left of the function-call operator, argument-dependent lookup is inhibited.

In practice, they may be implemented as function objects, or with special compiler extensions. No cap.


🌸 PARAMETERS 🌸

  • i β€” an iterator
  • n β€” number of elements to advance
  • bound β€” sentinel denoting the end of the range i points to

πŸ‘‘ RETURN VALUE πŸ‘‘

  1. The successor of iterator i.
  2. The n^th successor of iterator i.
  3. The first iterator equivalent to bound.
  4. The n^th successor of iterator i, or the first iterator equivalent to bound, whichever is first.

πŸ”₯ COMPLEXITY πŸ”₯

  1. Constant.
  2. Constant if I models std::random_access_iterator; otherwise linear.
  3. Constant if I and S models both std::random_access_iterator<I> and std::sized_sentinel_for<S, I>, or if I and S models std::assignable_from<I&, S>; otherwise linear.
  4. Constant if I and S models both std::random_access_iterator<I> and std::sized_sentinel_for<S, I>; otherwise linear.

🧚 POSSIBLE IMPLEMENTATION 🧚

Here’s the little possible-implementation moment, preserved with love and zero edits to the facts:

struct next_fn
{
    template<std::input_or_output_iterator I>
    constexpr I operator()(I i) const
    {
        ++i;
        return i;
    }

    template<std::input_or_output_iterator I>
    constexpr I operator()(I i, std::iter_difference_t<I> n) const
    {
        ranges::advance(i, n);
        return i;
    }

    template<std::input_or_output_iterator I, std::sentinel_for<I> S>
    constexpr I operator()(I i, S bound) const
    {
        ranges::advance(i, bound);
        return i;
    }

    template<std::input_or_output_iterator I, std::sentinel_for<I> S>
    constexpr I operator()(I i, std::iter_difference_t<I> n, S bound) const
    {
        ranges::advance(i, n, bound);
        return i;
    }
};

inline constexpr auto next = next_fn();

🫢 NOTES 🫢

Okay bestie, this is the part where the lore gets real. Although the expression ++x.begin() often compiles, it is not guaranteed to do so: x.begin() is an rvalue expression, and there is no requirement that specifies that increment of an rvalue is guaranteed to work. In particular, when iterators are implemented as pointers or its operator++ is lvalue-ref-qualified, ++x.begin() does not compile, while ranges::next(x.begin()) does. So yeah β€” this is not me being dramatic, this is the actual rule. πŸ’€

Lowkey important: ++x.begin() compiling is not a promise. Do not treat β€œoften compiles” as β€œalways valid,” bestie.

🌈 EXAMPLE 🌈

// Run this code

#include <cassert>
#include <iterator>

int main()
{
    auto v = {3, 1, 4};
    {
        auto n = std::ranges::next(v.begin());
        assert(*n == 1);
    }
    {
        auto n = std::ranges::next(v.begin(), 2);
        assert(*n == 4);
    }
    {
        auto n = std::ranges::next(v.begin(), v.end());
        assert(n == v.end());
    }
    {
        auto n = std::ranges::next(v.begin(), 42, v.end());
        assert(n == v.end());
    }
}

The example ate, honestly. {3, 1, 4} girlies rise. πŸ’…


⭐ SEE ALSO ⭐

  • ranges::prev β€” decrement an iterator by a given distance or to a bound; (C++20) (niebloid)
  • ranges::advance β€” advances an iterator by given distance or to a given bound; (C++20) (niebloid)
  • next β€” increment an iterator; (C++11) (function template)

Source: http://cppreference.com Β· version/date: 2024.06.10 β€” that’s the cute little sign-off, bestie. Go forth and increment responsibly. πŸŽ€βœ¨πŸ¦‹

πŸ“¦ where this page comes from