std::ranges...:find_if_not(3)

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

πŸ¦‹ std::ranges...:find_if_not(3) β€” from the C++ Standard Libary manual πŸ’…

🌸 NAME 🌸

std::ranges::find,std::ranges::find_if,std::ranges::find_if_not β€” std::ranges::find,std::ranges::find_if,std::ranges::find_if_not

Bestie, it’s the whole find / find_if / find_if_not trio in ranges form. Iconic behavior honestly.

✨ Synopsis ✨

First things first: it’s all Defined in header <algorithm>, and here’s the call signature situation exactly as the manual serves it:

          Defined in header <algorithm>
          Call signature
          template< std::input_iterator I, std::sentinel_for<I> S,

                    class T, class Proj = std::identity >
       (since
          requires std::indirect_binary_predicate
       C++20)
                       <ranges::equal_to, std::projected<I, Proj>,
       (until
          const T*>
       C++26)

          constexpr I find( I first, S last, const T& value, Proj proj
          = {} );
          template< std::input_iterator I, std::sentinel_for<I> S,

                    class Proj = std::identity,
                    class T = std::projected_value_t<I, Proj> >
          requires std::indirect_binary_predicate
       (since
                       <ranges::equal_to, std::projected<I, Proj>,
       C++26)
          const T*>

          constexpr I find( I first, S last, const T& value, Proj proj
          = {} );
          template< ranges::input_range R, class T, class Proj =
          std::identity >

          requires std::indirect_binary_predicate
       (since
                       <ranges::equal_to,
       C++20)
                        std::projected<ranges::iterator_t<R>, Proj>,
       (until
          const T*>
       C++26)
          constexpr ranges::borrowed_iterator_t<R>

              find( R&& r, const T& value, Proj proj = {} );
          template< ranges::input_range R, class Proj = std::identity,

                    class T =
          std::projected_value_t<ranges::iterator_t<R>, Proj> >
          requires std::indirect_binary_predicate
       (since
                       <ranges::equal_to,
       C++26)
                        std::projected<ranges::iterator_t<R>, Proj>,   (1)
          const T*>
          constexpr ranges::borrowed_iterator_t<R>

              find( R&& r, const T& value, Proj proj = {} );
          template< std::input_iterator I, std::sentinel_for<I> S,

                    class Proj = std::identity,
                    std::indirect_unary_predicate<std::projected<I,
       (3)     (since
          Proj>> Pred >
       C++20)
                                                                           (2)
          constexpr I find_if( I first, S last, Pred pred, Proj proj =
          {} );
          template< ranges::input_range R, class Proj = std::identity,

                    std::indirect_unary_predicate
                        <std::projected<ranges::iterator_t<R>, Proj>>
       (4)     (since
          Pred >
       C++20)
          constexpr ranges::borrowed_iterator_t<R>

              find_if( R&& r, Pred pred, Proj proj = {} );
          template< std::input_iterator I, std::sentinel_for<I> S,

                    class Proj = std::identity,
                    std::indirect_unary_predicate<std::projected<I,
       (5)     (since
          Proj>> Pred >
       C++20)

          constexpr I find_if_not( I first, S last, Pred pred, Proj
          proj = {} );
          template< ranges::input_range R, class Proj = std::identity,

                    std::indirect_unary_predicate
                        <std::projected<ranges::iterator_t<R>, Proj>>
       (6)     (since
          Pred >
       C++20)
          constexpr ranges::borrowed_iterator_t<R>

              find_if_not( R&& r, Pred pred, Proj proj = {} );

Okay so the whole point: these return the first element in the range [first, last) that satisfies the specific criteria:

  • 1) find searches for an element equal to value.
  • 3) find_if searches for an element for which predicate pred returns true.
  • 5) find_if_not searches for an element for which predicate pred returns false.
  • 2,4,6) Same as (1,3,5), but uses r as the source range, as if using ranges::begin(r) as first and ranges::end(r) as last.

Now the vibe check: 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 πŸŽ€

  • first, last β€” the range of elements to examine
  • r β€” the range of the elements to examine
  • value β€” value to compare the elements to
  • pred β€” predicate to apply to the projected elements
  • proj β€” projection to apply to the elements

πŸ¦‹ Return value πŸ¦‹

Iterator to the first element satisfying the condition, or iterator equal to last if no such element is found. Periodt.

πŸ”₯ Complexity πŸ”₯

At most last - first applications of the predicate and projection. That’s the budget, bestie.

πŸ‘‘ Possible implementation πŸ‘‘

✨ find ✨

        struct find_fn
        {
            template<std::input_iterator I, std::sentinel_for<I> S,
                     class Proj = std::identity,
                     class T = std::projected_value_t<I, Proj>>
            requires std::indirect_binary_predicate
                         <ranges::equal_to, std::projected<I, Proj>, const T*>
            constexpr I operator()(I first, S last, const T& value, Proj proj =
       {}) const
            {
                for (; first != last; ++first)
                    if (std::invoke(proj, *first) == value)
                        return first;
                return first;
            }

            template<ranges::input_range R, class T, class Proj =
       std::identity>
            requires std::indirect_binary_predicate<ranges::equal_to,
                         std::projected<ranges::iterator_t<R>, Proj>, const T*>
            constexpr ranges::borrowed_iterator_t<R>
                operator()(R&& r, const T& value, Proj proj = {}) const
            {
                return (*this)(ranges::begin(r), ranges::end(r), value,
       std::ref(proj));
            }
        };

        inline constexpr find_fn find;

✨ find_if ✨

        struct find_if_fn
        {
            template<std::input_iterator I, std::sentinel_for<I> S, class Proj
       = std::identity,
                     std::indirect_unary_predicate<std::projected<I, Proj>>
       Pred>
            constexpr I operator()(I first, S last, Pred pred, Proj proj = {})
       const
            {
                for (; first != last; ++first)
                    if (std::invoke(pred, std::invoke(proj, *first)))
                        return first;
                return first;
            }

            template<ranges::input_range R, class Proj = std::identity,
                     std::indirect_unary_predicate
                         <std::projected<ranges::iterator_t<R>, Proj>> Pred>
            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 find_if_fn find_if;

✨ find_if_not ✨

        struct find_if_not_fn
        {
            template<std::input_iterator I, std::sentinel_for<I> S, class Proj
       = std::identity,
                     std::indirect_unary_predicate<std::projected<I, Proj>>
       Pred>
            constexpr I operator()(I first, S last, Pred pred, Proj proj = {})
       const
            {
                for (; first != last; ++first)
                    if (!std::invoke(pred, std::invoke(proj, *first)))
                        return first;
                return first;
            }

            template<ranges::input_range R, class Proj = std::identity,
                     std::indirect_unary_predicate
                         <std::projected<ranges::iterator_t<R>, Proj>> Pred>
            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 find_if_not_fn find_if_not;

⭐ Notes ⭐

Feature-test macro tea:

  • Feature-test macro: __cpp_lib_algorithm_default_value_type β€” Value: 202403 β€” Std: (C++26) β€” Feature: List-initialization for algorithms (1,2)

πŸ’» EXAMPLE πŸ’»

okay bestie, this demo is literally the whole vibe β€” it’s got projector_example(), main(), the std::complex moment, all of it. Run it and the output below is exactly what you get. πŸ’…βœ¨

// Run this code

#include <algorithm>
#include <cassert>
#include <complex>
#include <format>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>

void projector_example()
{
    struct folk_info
    {
        unsigned uid;
        std::string name, position;
    };

    std::vector<folk_info> folks
    {
        {0, "Ana", "dev"},
        {1, "Bob", "devops"},
        {2, "Eve", "ops"}
    };

    const auto who{"Eve"};
    if (auto it = std::ranges::find(folks, who, &folk_info::name); it != folks.end())
        std::cout << std::format("Profile:\n"
                                 "    UID: {}\n"
                                 "    Name: {}\n"
                                 "    Position: {}\n\n",
                                 it->uid, it->name, it->position);
}

int main()
{
    namespace ranges = std::ranges;

    projector_example();

    const int n1 = 3;
    const int n2 = 5;
    const auto v = {4, 1, 3, 2};

    if (ranges::find(v, n1) != v.end())
        std::cout << "v contains: " << n1 << '\n';
    else
        std::cout << "v does not contain: " << n1 << '\n';

    if (ranges::find(v.begin(), v.end(), n2) != v.end())
        std::cout << "v contains: " << n2 << '\n';
    else
        std::cout << "v does not contain: " << n2 << '\n';

    auto is_even = [](int x) { return x % 2 == 0; };

    if (auto result = ranges::find_if(v.begin(), v.end(), is_even); result != v.end())
        std::cout << "First even element in v: " << *result << '\n';
    else
        std::cout << "No even elements in v\n";

    if (auto result = ranges::find_if_not(v, is_even); result != v.end())
        std::cout << "First odd element in v: " << *result << '\n';
    else
        std::cout << "No odd elements in v\n";

    auto divides_13 = [](int x) { return x % 13 == 0; };

    if (auto result = ranges::find_if(v, divides_13); result != v.end())
        std::cout << "First element divisible by 13 in v: " << *result << '\n';
    else
        std::cout << "No elements in v are divisible by 13\n";

    if (auto result = ranges::find_if_not(v.begin(), v.end(), divides_13);
        result != v.end())
        std::cout << "First element indivisible by 13 in v: " << *result << '\n';
    else
        std::cout << "All elements in v are divisible by 13\n";

    std::vector<std::complex<double>> nums{{4, 2}};
    #ifdef __cpp_lib_algorithm_default_value_type
        // T gets deduced in (2) making list-initialization possible
        const auto it = ranges::find(nums, {4, 2});
    #else
        const auto it = ranges::find(nums, std::complex<double>{4, 2});
    #endif
    assert(it == nums.begin());
}

πŸ“€ Output πŸ“€

and the output is giving exactly this:

Profile:
    UID: 2
    Name: Eve
    Position: ops

v contains: 3
v does not contain: 5
First even element in v: 4
First odd element in v: 1
No elements in v are divisible by 13
First element indivisible by 13 in v: 4

🌈 SEE ALSO 🌈

  • ranges::adjacent_find β€” finds the first two adjacent items that are equal (or satisfy a given predicate); (C++20), (niebloid).
  • ranges::find_end β€” finds the last sequence of elements in a certain range; (C++20), (niebloid).
  • ranges::find_first_of β€” searches for any one of a set of elements; (C++20), (niebloid).
  • ranges::mismatch β€” finds the first position where two ranges differ; (C++20), (niebloid).
  • ranges::search β€” searches for a range of elements; (C++20), (niebloid).
  • find, find_if, find_if_not β€” finds the first element satisfying specific criteria; find_if_not is (function template), (C++11).

that’s the page, bestie 🫢 source is cppreference.com, dated/versioned 2024.06.10, page std::ranges...:find_if_not(3). mwah πŸ’‹πŸŽ€

πŸ“¦ where this page comes from