Spec-Zone.ru › C++

std::ranges::clamp

Определено в заголовке <algorithm>
Подпись вызова
template< class T, class Proj = std::identity,
          std::indirect_strict_weak_order<std::projected<const T*, Proj>> Comp =
              ranges::less >
constexpr const T&
    clamp( const T& v, const T& lo, const T& hi, Comp comp = {}, Proj proj = {} );
(с C++20)

Если v меньше, чем lo, возвращает lo; в противном случае, если hi меньше, чем v, возвращает hi; в противном случае возвращает v.

Поведение не определено, если lo больше, чем hi.

Функциональные сущности, описанные на этой странице, являются niebloids, то есть:

  • Явные списки аргументов шаблона не могут быть указаны при вызове ни одного из них.
  • Ни один из них не виден для поиска аргументов, зависящих от имени.
  • Когда любой из них находится с помощью обычного неупоминаемого поиска в качестве имени слева от оператора вызова функции, поиск аргументов, зависящих от имени, ингибируется.

На практике они могут быть реализованы как объекты функций или с помощью специальных расширений компилятора.

Параметры

v - значение для прижимания
lo, hi - границы для прижимания v к
comp - сравнение, применяемое к спроецированным элементам
proj - проекция, применяемая к v, lo и hi

Возвращаемое значение

Ссылка на lo, если спроецированное значение v меньше спроецированного значения lo, ссылка на hi, если спроецированное значение hi меньше спроецированного значения v, в противном случае ссылка на v.

Сложность

Не более двух сравнений и трёх применений проекции.

Возможная реализация

struct clamp_fn
{
    template<class T, class Proj = std::identity,
             std::indirect_strict_weak_order<std::projected<const T*, Proj>>
                 Comp = std::ranges::less>
    constexpr const T& operator()(const T& v, const T& lo, const T& hi,
                                  Comp comp = {}, Proj proj = {}) const
    {
        auto&& pv = std::invoke(proj, v);
 
        if (std::invoke(comp, std::forward<decltype(pv)>(pv), std::invoke(proj, lo)))
            return lo;
 
        if (std::invoke(comp, std::invoke(proj, hi), std::forward<decltype(pv)>(pv)))
            return hi;
 
        return v;
    }
};
 
inline constexpr clamp_fn clamp;

Примечания

Получение результата std::ranges::clamp по ссылке создаёт висячую ссылку, если один из параметров является временным, и этот параметр возвращается:
int n = -1;
const int& r = std::ranges::clamp(n, 0, 255); // r is dangling

Если v эквивалентен любой из границ, возвращает ссылку на v, а не на границу.

Эта функция не должна использоваться с проекцией, возвращающей значение по значению, и компаратором, принимающим аргументы по значению, если перемещение из типа результата проекции в тип параметра компаратора эквивалентно копированию. Если сравнение с помощью std::invoke изменит результат проекции, поведение не определено из-за требований семантики std::regular_invocable (включая std::indirect_strict_weak_order).

Стандарт требует, чтобы категория значения результата проекции сохранялась, и proj может быть вызван на v только один раз, что означает, что результат проекции, являющийся prvalue, должен быть кэширован и перемещён дважды для двух вызовов компаратора.

  • libstdc++ не соответствует этому и всегда передаёт результат проекции как lvalue.
  • libc++ не соответствует этому, выполняя проекцию дважды.
  • MSVC STL раньше выполняла проекцию дважды, что было исправлено в VS 2022 17.2.

Пример

#include <algorithm>
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <string>
 
using namespace std::literals;
namespace ranges = std::ranges;
 
int main()
{
    for (std::cout << " raw   clamped to int8_t   clamped to uint8_t\n";
         int const v : {-129, -128, -1, 0, 42, 127, 128, 255, 256})
        std::cout << std::setw(04) << v
                  << std::setw(20) << ranges::clamp(v, INT8_MIN, INT8_MAX)
                  << std::setw(21) << ranges::clamp(v, 0, UINT8_MAX) << '\n';
    std::cout << '\n';
 
    // Projection function
    const auto stoi = [](std::string s) { return std::stoi(s); };
 
    // Same as above, but with strings
    for (std::string const v : {"-129", "-128", "-1", "0", "42",
                                "127", "128", "255", "256"})
        std::cout << std::setw(04) << v
                  << std::setw(20) << ranges::clamp(v, "-128"s, "127"s, {}, stoi)
                  << std::setw(21) << ranges::clamp(v, "0"s, "255"s, {}, stoi)
                  << '\n';
}

Вывод:

 raw   clamped to int8_t   clamped to uint8_t
-129                -128                    0
-128                -128                    0
  -1                  -1                    0
   0                   0                    0
  42                  42                   42
 127                 127                  127
 128                 127                  128
 255                 127                  255
 256                 127                  255
 
-129                -128                    0
-128                -128                    0
  -1                  -1                    0
   0                   0                    0
  42                  42                   42
 127                 127                  127
 128                 127                  128
 255                 127                  255
 256                 127                  255

См. также

ranges::min
(C++20)
возвращает меньшее из заданных значений
(niebloid)
ranges::max
(C++20)
возвращает большее из заданных значений
(niebloid)
in_range
(C++20)
проверяет, является ли целое значение в диапазоне заданного целочисленного типа
(шаблон функции)
clamp
(C++17)
прижимает значение между двумя границами
(шаблон функции)

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/algorithm/ranges/clamp

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API