std::ranges::adjacent_find(3)
β¨ std::ranges::adjacent_find β¨
omg bestie, okay so this is literally one of those C++ Standard Library pages β std::ranges::adjacent_find(3) β and it's giving "find me two consecutive besties who are exactly the same" energy π
no cap.
πΈ NAME πΈ
std::ranges::adjacent_find β okay so this is the iconic algorithm that finds the first two adjacent elements in a range that are equal (or satisfy some predicate you give it). it's lowkey obsessed with consecutive pairs, fr fr.
π Synopsis π
So this is defined in the header <algorithm> (iykyk), and there are two call signatures β both since C++20, periodt.
template< std::forward_iterator I, std::sentinel_for<I> S, class Proj = std::identity,
std::indirect_binary_predicate<
std::projected<I, Proj>,
std::projected<I, Proj>> Pred = ranges::equal_to >
constexpr I
adjacent_find( I first, S last, Pred pred = {}, Proj proj = {} );
(this one's numbered (1) btw, since C++20)
template< ranges::forward_range R, class Proj = std::identity,
std::indirect_binary_predicate<
std::projected<ranges::iterator_t<R>, Proj>,
std::projected<ranges::iterator_t<R>, Proj>> Pred = ranges::equal_to >
constexpr ranges::borrowed_iterator_t<R>
adjacent_find( R&& r, Pred pred = {}, Proj proj = {} );
(and this one's (2), also since C++20)
Okay so what does it actually do? It searches the range [first, last) for two consecutive equal elements. Like, it's literally looking for twin vibes in your data π
- Elements are compared using
predβ but wait, there's a glow-up first: they're projected with the projectionprojbefore comparison. so it'spredon the projected elements, got it? - Same as (1), but uses
ras the source range, as if usingranges::begin(r)asfirstandranges::end(r)aslast. same energy, different syntax β¨
π«Ά Also important bestie: the function-like entities described on this page are niebloids, which means: - 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. (the compiler girlies are doing their thing behind the scenes π)
π Parameters π
first,lastβ the range of elements to examine (like, the whole squad you're checking)rβ the range of the elements to examine (same thing, but as a range object instead of iterators)predβ predicate to apply to the projected elements (the vibe check function)projβ projection to apply to the elements (the glow-up filter before the vibe check)
π Return value π
An iterator to the first of the first pair of identical elements β that is, the first iterator it such that:
bool(std::invoke(pred, std::invoke(proj1, *it),
std::invoke(proj, *(it + 1)))) is true.
(yeah, it says proj1 in the original, that's not a typo on my part β copy-pasting the exact thing bestie, don't come for me π)
If no such elements are found, an iterator equal to last is returned. so basically "no twin vibes found, here's the end, bye π"
π¦ Complexity π¦
Exactly min((result - first) + 1, (last - first) - 1) applications of the predicate and projection β where result is the return value. so it's efficient, it's not doing extra work, it's literally that girl who gets in and gets out π₯
π§ Possible implementation π§
okay so here's how it might be implemented under the hood β bestie this is the whole struct, verbatim, don't @ me:
struct adjacent_find_fn
{
template<std::forward_iterator I, std::sentinel_for<I> S, class Proj = std::identity,
std::indirect_binary_predicate<
std::projected<I, Proj>,
std::projected<I, Proj>> Pred = ranges::equal_to>
constexpr I operator()(I first, S last, Pred pred = {}, Proj proj = {}) const
{
if (first == last)
return first;
auto next = ranges::next(first);
for (; next != last; ++next, ++first)
if (std::invoke(pred, std::invoke(proj, *first), std::invoke(proj, *next)))
return first;
return next;
}
template<ranges::forward_range R, class Proj = std::identity,
std::indirect_binary_predicate<
std::projected<ranges::iterator_t<R>, Proj>,
std::projected<ranges::iterator_t<R>, Proj>> Pred = ranges::equal_to>
constexpr ranges::borrowed_iterator_t<R>
operator()(R&& r, Pred pred = {}, Proj proj = {}) const
{
return (*this)(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
}
};
inline constexpr adjacent_find_fn adjacent_find;
like, it's literally walking through the range two-at-a-time and checking if the projected values satisfy pred. iconic behavior honestly π
π Example π
okay bestie, here's a whole example β you can literally run this code, it's giving "demo energy":
#include <algorithm>
#include <functional>
#include <iostream>
#include <ranges>
constexpr bool some_of(auto&& r, auto&& pred) // some but not all
{
return std::ranges::cend(r) != std::ranges::adjacent_find(r,
[&pred](auto const& x, auto const& y)
{
return pred(x) != pred(y);
});
}
// test some_of
constexpr auto a = {0, 0, 0, 0}, b = {1, 1, 1, 0}, c = {1, 1, 1, 1};
auto is_one = [](auto x){ return x == 1; };
static_assert(!some_of(a, is_one) && some_of(b, is_one) && !some_of(c, is_one));
int main()
{
const auto v = {0, 1, 2, 3, 40, 40, 41, 41, 5}; /*
^^ ^^ */
namespace ranges = std::ranges;
if (auto it = ranges::adjacent_find(v.begin(), v.end()); it == v.end())
std::cout << "No matching adjacent elements\n";
else
std::cout << "The first adjacent pair of equal elements is at ["
<< ranges::distance(v.begin(), it) << "] == " << *it << '\n';
if (auto it = ranges::adjacent_find(v, ranges::greater()); it == v.end())
std::cout << "The entire vector is sorted in ascending order\n";
else
std::cout << "The last element in the non-decreasing subsequence is at ["
<< ranges::distance(v.begin(), it) << "] == " << *it << '\n';
}
And the output is literally:
The first adjacent pair of equal elements is at [4] == 40
The last element in the non-decreasing subsequence is at [7] == 41
see how it found the 40, 40 pair first? and then with ranges::greater() it found where the non-decreasing streak breaks? chef's kiss π₯π
π See also π
ranges::uniqueβ removes consecutive duplicate elements in a range. (C++20) (niebloid) β like, it's the one that actually removes the duplicates, not just finds them πadjacent_findβ finds the first two adjacent items that are equal (or satisfy a given predicate). (function template) β the OG non-ranges version, still iconic in its own way β¨
okay bestie that's literally the whole page π you're gonna slay with adjacent_find now, no cap. this was std::ranges::adjacent_find(3) from the C++ Standard Library, brought to you by the girlies at http://cppreference.com β page dated 2024.06.10 πΈ go forth and find those adjacent twins, periodt π«Ά
π¦ where this page comes from
- nixpkgs unstable: stdman 2024.07.05
- opensuse-leap 16.0: stdman 2024.07.05-bp160.1.9 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms