std::ranges::clamp() algorithm
- od C++20
- Simplified
- Detailed
// (1)
constexpr const T&
clamp( const T& v, const T& lo, const T& hi, Comp comp = {}, Proj proj = {} );
The type of arguments are generic and have the following constraints:
T
- (none)Proj
- (none)Comp
-std::indirect_strict_weak_order<std::projected<const T*, Proj>>
// (1)
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 = {} );
-
If
v
compares less thanlo
, returnslo
. -
Otherwise, if
v
compares more thanhi
, returnshi
. -
Otherwise returns
v
. -
(1) Uses
operator<
to compare the values. -
(2) Same as (1), but uses
comp
to compare the values.
The behavior is undefined
if the value oflo
is greater than hi
.Parameters
v | The value to clamp. |
lo hi | The range of elements to clamp. |
comp | The comparison to apply to the projected elements. |
proj | The projection to apply to |
Return value
Reference to lo
if the projected value of v
is less than the projected value of lo
,
reference to hi
if the projected value of hi
is less than the projected value of v
, otherwise reference to v
.
Complexity
At most two comparisons and three applications of the projection.
Exceptions
(none)
Possible implementation
clamp(1) and clamp(2)
struct clamp_fn
{
template<class T, class Proj = std::identity,
std::indirect_strict_weak_order<std::projected<const T*, Proj>>
Comp = 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);
return
std::invoke(comp, std::forward<decltype(pv)>(pv), std::invoke(proj, lo))
? lo
: std::invoke(comp, std::invoke(proj, hi), std::forward<decltype(pv)>(pv))
? hi
: v;
}
};
inline constexpr clamp_fn clamp;
Notes
Capturing the result of std::ranges::clamp
by reference produces a dangling reference if one of the parameters is a temporary and that parameter is returned:
int n = 1;
const int& r = std::ranges::clamp(n - 1, n + 1); // r is dangling
If v
compares equivalent to either bound, returns a reference to v
, not the bound.
Examples
#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
Hover to see the original license.