Namespaces
Variants

std::ranges::minmax, std::ranges::minmax_result

From cppreference.com
 
 
Algorithm library
Constrained algorithms and algorithms on ranges (C++20)
Constrained algorithms, e.g. ranges::copy, ranges::sort, ...
Non-modifying sequence operations    
Batch operations
(C++17)
Search operations
Modifying sequence operations
Copy operations
(C++11)
(C++11)
Swap operations
Transformation operations
Generation operations
Removing operations
Order-changing operations
(until C++17)(C++11)
(C++20)(C++20)
Sampling operations
(C++17)

Sorting and related operations
Partitioning operations
(C++11)    

Sorting operations
Binary search operations
(on partitioned ranges)
Set operations (on sorted ranges)
Merge operations (on sorted ranges)
Heap operations
Minimum/maximum operations
(C++11)
(C++17)
Lexicographical comparison operations
Permutation operations


 
Constrained algorithms
All names in this menu belong to namespace std::ranges
Non-modifying sequence operations
Fold operations (Helper templates)
Modifying sequence operations
Partitioning operations
Sorting operations
Binary search operations (on sorted ranges)
       
       
Set operations (on sorted ranges)
Heap operations
Minimum/maximum operations
       
       
Permutation operations
Specialized <memory> algorithms
Return types
 
Defined in header <algorithm>
Call signature
template< class T, class Proj = std::identity,
          std::indirect_strict_weak_order
              <std::projected<const T*, Proj>> Comp = ranges::less >
constexpr ranges::minmax_result<const T&>
    minmax( const T& a, const T& b, Comp comp = {}, Proj proj = {} );
(1) (since C++20)
template< std::copyable T, class Proj = std::identity,
          std::indirect_strict_weak_order
              <std::projected<const T*, Proj>> Comp = ranges::less >
constexpr ranges::minmax_result<T>
    minmax( std::initializer_list<T> r, Comp comp = {}, Proj proj = {} );
(2) (since C++20)
template< ranges::input_range R, class Proj = std::identity,
          std::indirect_strict_weak_order
              <std::projected<ranges::iterator_t<R>,
                              Proj>> Comp = ranges::less >
    requires std::indirectly_copyable_storable<ranges::iterator_t<R>,
                                               ranges::range_value_t<R>*>
constexpr ranges::minmax_result<ranges::range_value_t<R>>
    minmax( R&& r, Comp comp = {}, Proj proj = {} );
(3) (since C++20)
template< /*execution-policy*/ Ep, /*sized-random-access-range*/ R,
          class Proj = identity,
          std::indirect_strict_weak_order
              <std::projected<ranges::iterator_t<R>,
                              Proj>> Comp = ranges::less >
    requires std::indirectly_copyable_storable<ranges::iterator_t<R>,
                                               ranges::range_value_t<R>*>
ranges::minmax_result<ranges::range_value_t<R>>
    minmax( Ep&& policy, R&& r, Comp comp = {}, Proj proj = {} );
(4) (since C++26)
Helper types
template< class T >
using minmax_result = ranges::min_max_result<T>;
(5) (since C++20)

For the definition of /*execution-policy*/, see this page; for the definition of /*sized-random-access-range*/, see this page.

Returns the smallest and the greatest of the given values (projected by proj), the projected values are compared using comp.

1) Returns the smaller and greater of a and b in order.
2-4) Returns the first smallest value and the first largest value in the initializer list or target range r.
If ranges::distance(r) is zero, the behavior is undefined.
4) Same as (3), but executed according to policy.

The function-like entities described on this page are algorithm function objects (informally known as niebloids), that is:

Parameters

a, b - the values to compare
r - the range of values to compare
comp - the comparator to be applied to the (projected) elements
proj - the projection to be applied to the elements
policy - the execution policy to use

Return value

1) A ranges::minmax_result object, where:
  • The data member min holds b is b is smaller than a, or a otherwise.
  • The data member max holds a is b is smaller than a, or b otherwise.
2-4) A ranges::minmax_result object, where:
  • The data member min holds the smallest value in ilist. If there are multiple smallest values, min holds the first one of them.
  • The data member max holds the largest value in ilist. If there are multiple largest values, max holds the last one of them.

Complexity

1) Exactly one application of comp, and two applications of proj.
2-4) Given N as ranges::distance(r):
2,3) At most
3N
2
applications of comp, and twice as many applications of proj.
4) 𝓞(N) applications of comp, and twice as many applications of proj.

Exceptions

4) During the execution process:
  • If the temporary memory resources required for parallelization are not available, std::bad_alloc is thrown.
  • If an uncaught exception is thrown while accessing objects via an algorithm argument, the behavior is determined by the execution policy (for standard policies, std::terminate is invoked).

Notes

For overload (1), if one of the parameters is a temporary, the reference returned becomes a dangling reference at the end of the full expression that contains the call to minmax:

int n = 1;
auto p = std::ranges::minmax(n, n + 1);
int m = p.min; // ok
int x = p.max; // undefined behavior
 
// Note that structured bindings have the same issue
auto [mm, xx] = std::ranges::minmax(n, n + 1);
xx; // undefined behavior

Possible implementation

struct minmax_fn
{
    template<class T, class Proj = std::identity,
             std::indirect_strict_weak_order
                 <std::projected<const T*, Proj>> Comp = ranges::less>
    constexpr ranges::minmax_result<const T&>
         operator()(const T& a, const T& b, Comp comp = {}, Proj proj = {}) const
    {
        if (std::invoke(comp, std::invoke(proj, b), std::invoke(proj, a)))
            return {b, a};
        return {a, b};
    }
    
    template<std::copyable T, class Proj = std::identity,
             std::indirect_strict_weak_order
                 <std::projected<const T*, Proj>> Comp = ranges::less>
    constexpr ranges::minmax_result<T>
        operator()(std::initializer_list<T> r, Comp comp = {}, Proj proj = {}) const
    {
        auto result = ranges::minmax_element(r, std::ref(comp), std::ref(proj));
        return {*result.min, *result.max};
    }
    
    template<ranges::input_range R, class Proj = std::identity,
             std::indirect_strict_weak_order
                 <std::projected<ranges::iterator_t<R>, Proj>> Comp = ranges::less>
        requires std::indirectly_copyable_storable<ranges::iterator_t<R>,
                                                   ranges::range_value_t<R>*>
    constexpr ranges::minmax_result<ranges::range_value_t<R>>
        operator()(R&& r, Comp comp = {}, Proj proj = {}) const
    {
        auto first = ranges::begin(r);
        const auto last = ranges::end(r);
        
        auto min = static_cast<range_value_t<R>>(*first), max = min;
        while (++first != last)
        {
            auto x = static_cast<range_value_t<R>>(*first);
            if (++first == last)
            {
                if (std::invoke(comp, std::invoke(proj, x),
                                      std::invoke(proj, min)))
                    min = x;
                else if (!std::invoke(comp, std::invoke(proj, x),
                                            std::invoke(proj, max)))
                    max = x;
                break;
            }
            
            if (std::invoke(comp, std::invoke(proj, *first),
                                  std::invoke(proj, x)))
            {
                if (std::invoke(comp, std::invoke(proj, *first),
                                      std::invoke(proj, min)))
                    min = *first;
                if (!std::invoke(comp, std::invoke(proj, x),
                                       std::invoke(proj, max)))
                    max = x;
            }
            else
            {
                if (std::invoke(comp, std::invoke(proj, x),
                                      std::invoke(proj, min)))
                    min = x;
                if (!std::invoke(comp, std::invoke(proj, *first),
                                       std::invoke(proj, max)))
                    max = *first;
            }
        }
        
        return {min, max};
    }
};

inline constexpr minmax_fn minmax;

Example

#include <algorithm>
#include <array>
#include <iostream>
#include <random>

int main()
{
    namespace ranges = std::ranges;
    
    constexpr std::array v{3, 1, 4, 1, 5, 9, 2, 6, 5};
    
    std::random_device rd;
    std::mt19937_64 generator(rd());
    std::uniform_int_distribution<> distribution(0, ranges::distance(v)); // [0..9]
    
    // auto bounds = ranges::minmax(distribution(generator), distribution(generator));
    // UB: dangling references: bounds.min and bounds.max have the type “const int&”.
    
    const int x1 = distribution(generator);
    const int x2 = distribution(generator);
    auto bounds = ranges::minmax(x1, x2); // OK: got references to lvalues x1 and x2
    
    std::cout << "v[" << bounds.min << ":" << bounds.max << "]: ";
    for (int i = bounds.min; i < bounds.max; ++i)
        std::cout << v[i] << ' ';
    std::cout << '\n';
    
    auto [min, max] = ranges::minmax(v);
    std::cout << "smallest: " << min << ", " << "largest: " << max << '\n';
}

Possible output:

v[3:9]: 1 5 9 2 6 5 
smallest: 1, largest: 9

See also

(C++11)
returns the smaller and larger of two elements
(function template) [edit]
returns the smaller of the given values
(algorithm function object)[edit]
returns the greater of the given values
(algorithm function object)[edit]
returns the smallest and the largest elements in a range
(algorithm function object)[edit]
clamps a value between a pair of boundary values
(algorithm function object)[edit]