Skip to main content

std::ranges::lower_bound() algorithm

// (1)
constexpr I
lower_bound( I first, S last, const T& value, Comp comp = {}, Proj proj = {} );

// (2)
constexpr ranges::borrowed_iterator_t<R>
lower_bound( R&& r, const T& value, Comp comp = {}, Proj proj = {} );

The type of arguments are generic and have the following constraints:

  • I - std::forward_iterator

  • S - std::sentinel_for<I>

  • R - std::ranges::forward_range

  • Comp:

    • (1) - indirect_strict_weak_order< const T*, projected<I, Proj>>
    • (2) - indirect_strict_weak_order< const T*, projected<ranges::iterator_t<R>, Proj>>

    (The std:: namespace was ommitted here for readability)

  • T - (none)

  • Proj - (none)

The Proj and Comp template arguments have the following default types: std::identity, ranges::less for all overloads.

  • (1) Returns an iterator pointing to the first element in the range [first; last) that is not less than (i.e. greater or equal to) value, or last if no such element is found.

    The range [first; last) must be partitioned with respect to the expression comp(element, value), i.e., all elements for which the expression is true must precede all elements for which the expression is false.

    A fully-sorted range meets this criterion.

  • (2) Same as (1), but uses r as the source range, as if using ranges::begin(r) as first and ranges::end(r) as last.

The function-like entities described on this page are niebloids.

Parameters

first
last

The partially-ordered range of elements to examine.

r

The partially-ordered range of elements to examine.

value

The value to compare the elements to.

comp

Comparison predicate to apply to the projected elements.

proj

Projection to apply to the elements.

Return value

Iterator pointing to the first element that is not less than value, or last if no such element is found.

Complexity

At most log2(last - first) + O(1) comparisons and applications of the projection.

However, for an iterator that does not model random_access_iterator, the number of iterator increments is linear.

Exceptions

(none)

Possible implementation

ranges::lower_bound
struct lower_bound_fn
{
template<std::forward_iterator I, std::sentinel_for<I> S,
class T, class Proj = std::identity,
std::indirect_strict_weak_order<
const T*,
std::projected<I, Proj>> Comp = ranges::less>
constexpr I operator()(I first, S last, const T& value,
Comp comp = {}, Proj proj = {}) const
{
I it;
std::iter_difference_t<I> count, step;
count = std::ranges::distance(first, last);

while (count > 0)
{
it = first;
step = count / 2;
ranges::advance(it, step, last);
if (comp(std::invoke(proj, *it), value))
{
first = ++it;
count -= step + 1;
}
else
count = step;
}
return first;
}

template<ranges::forward_range R, class T, class Proj = std::identity,
std::indirect_strict_weak_order<
const T*,
std::projected<ranges::iterator_t<R>, Proj>> Comp = ranges::less>
constexpr ranges::borrowed_iterator_t<R>
operator()(R&& r, const T& value, Comp comp = {}, Proj proj = {}) const
{
return (*this)(ranges::begin(r), ranges::end(r), value,
std::ref(comp), std::ref(proj));
}
};

inline constexpr lower_bound_fn lower_bound;

Examples

Main.cpp
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

namespace ranges = std::ranges;

template<std::forward_iterator I, std::sentinel_for<I> S, class T,
class Proj = std::identity,
std::indirect_strict_weak_order<
const T*,
std::projected<I, Proj>> Comp = ranges::less>
constexpr
I binary_find(I first, S last, const T& value, Comp comp = {}, Proj proj = {})
{
first = ranges::lower_bound(first, last, value, comp, proj);
return first != last && !comp(value, proj(*first)) ? first : last;
}

int main()
{
std::vector data{1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5};
// ^^^^^^^^^^
auto lower = ranges::lower_bound(data, 4);
auto upper = ranges::upper_bound(data, 4);

std::cout << "found a range [" << ranges::distance(data.cbegin(), lower)
<< ", " << ranges::distance(data.cbegin(), upper) << ") = { ";
ranges::copy(lower, upper, std::ostream_iterator<int>(std::cout, " "));
std::cout << "}\n";

// classic binary search, returning a value only if it is present

data = {1, 2, 4, 8, 16};
// ^
auto it = binary_find(data.cbegin(), data.cend(), 8); // '5' would return end()

if (it != data.cend())
std::cout << *it << " found at index "<< ranges::distance(data.cbegin(), it);
}
Output
found a range [6, 10) = { 4 4 4 4 }
8 found at index 3
This article originates from this CppReference page. It was likely altered for improvements or editors' preference. Click "Edit this page" to see all changes made to this document.
Hover to see the original license.

std::ranges::lower_bound() algorithm

// (1)
constexpr I
lower_bound( I first, S last, const T& value, Comp comp = {}, Proj proj = {} );

// (2)
constexpr ranges::borrowed_iterator_t<R>
lower_bound( R&& r, const T& value, Comp comp = {}, Proj proj = {} );

The type of arguments are generic and have the following constraints:

  • I - std::forward_iterator

  • S - std::sentinel_for<I>

  • R - std::ranges::forward_range

  • Comp:

    • (1) - indirect_strict_weak_order< const T*, projected<I, Proj>>
    • (2) - indirect_strict_weak_order< const T*, projected<ranges::iterator_t<R>, Proj>>

    (The std:: namespace was ommitted here for readability)

  • T - (none)

  • Proj - (none)

The Proj and Comp template arguments have the following default types: std::identity, ranges::less for all overloads.

  • (1) Returns an iterator pointing to the first element in the range [first; last) that is not less than (i.e. greater or equal to) value, or last if no such element is found.

    The range [first; last) must be partitioned with respect to the expression comp(element, value), i.e., all elements for which the expression is true must precede all elements for which the expression is false.

    A fully-sorted range meets this criterion.

  • (2) Same as (1), but uses r as the source range, as if using ranges::begin(r) as first and ranges::end(r) as last.

The function-like entities described on this page are niebloids.

Parameters

first
last

The partially-ordered range of elements to examine.

r

The partially-ordered range of elements to examine.

value

The value to compare the elements to.

comp

Comparison predicate to apply to the projected elements.

proj

Projection to apply to the elements.

Return value

Iterator pointing to the first element that is not less than value, or last if no such element is found.

Complexity

At most log2(last - first) + O(1) comparisons and applications of the projection.

However, for an iterator that does not model random_access_iterator, the number of iterator increments is linear.

Exceptions

(none)

Possible implementation

ranges::lower_bound
struct lower_bound_fn
{
template<std::forward_iterator I, std::sentinel_for<I> S,
class T, class Proj = std::identity,
std::indirect_strict_weak_order<
const T*,
std::projected<I, Proj>> Comp = ranges::less>
constexpr I operator()(I first, S last, const T& value,
Comp comp = {}, Proj proj = {}) const
{
I it;
std::iter_difference_t<I> count, step;
count = std::ranges::distance(first, last);

while (count > 0)
{
it = first;
step = count / 2;
ranges::advance(it, step, last);
if (comp(std::invoke(proj, *it), value))
{
first = ++it;
count -= step + 1;
}
else
count = step;
}
return first;
}

template<ranges::forward_range R, class T, class Proj = std::identity,
std::indirect_strict_weak_order<
const T*,
std::projected<ranges::iterator_t<R>, Proj>> Comp = ranges::less>
constexpr ranges::borrowed_iterator_t<R>
operator()(R&& r, const T& value, Comp comp = {}, Proj proj = {}) const
{
return (*this)(ranges::begin(r), ranges::end(r), value,
std::ref(comp), std::ref(proj));
}
};

inline constexpr lower_bound_fn lower_bound;

Examples

Main.cpp
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

namespace ranges = std::ranges;

template<std::forward_iterator I, std::sentinel_for<I> S, class T,
class Proj = std::identity,
std::indirect_strict_weak_order<
const T*,
std::projected<I, Proj>> Comp = ranges::less>
constexpr
I binary_find(I first, S last, const T& value, Comp comp = {}, Proj proj = {})
{
first = ranges::lower_bound(first, last, value, comp, proj);
return first != last && !comp(value, proj(*first)) ? first : last;
}

int main()
{
std::vector data{1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5};
// ^^^^^^^^^^
auto lower = ranges::lower_bound(data, 4);
auto upper = ranges::upper_bound(data, 4);

std::cout << "found a range [" << ranges::distance(data.cbegin(), lower)
<< ", " << ranges::distance(data.cbegin(), upper) << ") = { ";
ranges::copy(lower, upper, std::ostream_iterator<int>(std::cout, " "));
std::cout << "}\n";

// classic binary search, returning a value only if it is present

data = {1, 2, 4, 8, 16};
// ^
auto it = binary_find(data.cbegin(), data.cend(), 8); // '5' would return end()

if (it != data.cend())
std::cout << *it << " found at index "<< ranges::distance(data.cbegin(), it);
}
Output
found a range [6, 10) = { 4 4 4 4 }
8 found at index 3
This article originates from this CppReference page. It was likely altered for improvements or editors' preference. Click "Edit this page" to see all changes made to this document.
Hover to see the original license.