std::ranges::clamp
| Definiert in Header <algorithm>
|
||
| Aufrufsignatur |
||
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 = {} );
|
(seit C++20) | |
Wenn der Wert von v innerhalb von [lo, hi] liegt, wird v zurückgegeben; andernfalls wird die nächste Grenze zurückgegeben.
Das Verhalten ist undefiniert, wenn lo größer als hi ist.
Die auf dieser Seite beschriebenen funktionsähnlichen Entitäten sind Algorithmus-Funktionsobjekte (informell als Niebloids bekannt), das heißt:
- Explizite Vorlagenargumentlisten können beim Aufruf keiner von ihnen angegeben werden.
- Keine von ihnen ist für argumentabhängige Suche sichtbar.
- Wenn eine von ihnen durch normale unqualifizierte Suche als Name links vom Funktionsaufrufoperator gefunden wird, wird argumentabhängige Suche unterdrückt.
Parameter
| v | - | der zu begrenzende Wert |
| lo, hi | - | die Grenzen, auf die v begrenzt werden soll
|
| comp | - | der auf die projizierten Elemente anzuwendende Vergleich |
| proj | - | die auf v, lo und hi
|
anzuwendende Projektion
Referenz auf lo, wenn der projizierte Wert von v kleiner als der projizierte Wert von lo ist, Referenz auf hi, wenn der projizierte Wert von hi kleiner als der projizierte Wert von v ist, ansonsten Referenz auf v.
Komplexität
Höchstens zwei Vergleiche und drei Anwendungen der Projektion.
Mögliche Implementierung
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;
|
Anmerkungen
Das Erfassen des Ergebnisses von std::ranges::clamp per Referenz erzeugt eine hängende Referenz, wenn einer der Parameter ein temporäres Objekt ist und dieser Parameter zurückgegeben wird:
int n = -1;
const int& r = std::ranges::clamp(n, 0, 255); // r is dangling
Wenn v äquivalent zu einer der Grenzen verglichen wird, wird eine Referenz auf v zurückgegeben, nicht auf die Grenze.
Diese Funktion sollte nicht mit einer Projektion, die per Wert zurückgibt, und einem Vergleich verwendet werden, der Argumente per Wert annimmt, es sei denn, eine Verschiebung vom Projektionsergebnis-Typ zum Vergleichsparameter-Typ ist äquivalent zu einer Kopie. Wenn der Vergleich über std::invoke das Ergebnis der Projektion ändern würde, ist das Verhalten aufgrund von den semantischen Anforderungen von std::regular_invocable (subsumiert durch std::indirect_strict_weak_order) undefiniert.
Der Standard verlangt, dass die Wertkategorie des Ergebnisses der Projektion erhalten bleibt, und proj kann nur einmal auf v aufgerufen werden, was bedeutet, dass ein Projektionsergebnis, das ein Prvalue ist, für die beiden Aufrufe des Vergleichs zwischengespeichert und zweimal verschoben werden muss.
- libstdc++ entspricht dem nicht und übergibt das Projektionsergebnis immer als Lvalue.
- libc++ führte die Projektion früher zweimal aus, was in Clang 18 korrigiert wurde.
- MSVC STL führte die Projektion früher zweimal aus, was in VS 2022 17.2 korrigiert wurde.
Beispiel
#include <algorithm>
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <string>
using namespace std::literals;
namespace ranges = std::ranges;
int main()
{
std::cout << "[raw] [" << INT8_MIN << ',' << INT8_MAX << "] "
"[0" << ',' << UINT8_MAX << "]\n";
for (int const v : {-129, -128, -1, 0, 42, 127, 128, 255, 256})
std::cout << std::setw(4) << v
<< std::setw(11) << ranges::clamp(v, INT8_MIN, INT8_MAX)
<< std::setw(8) << ranges::clamp(v, 0, UINT8_MAX) << '\n';
std::cout << std::string(23, '-') << '\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(4) << v
<< std::setw(11) << ranges::clamp(v, "-128"s, "127"s, {}, stoi)
<< std::setw(8) << ranges::clamp(v, "0"s, "255"s, {}, stoi)
<< '\n';
}
Ausgabe:
[raw] [-128,127] [0,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
-----------------------
-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
Siehe auch
(C++20) |
gibt den kleineren der gegebenen Werte zurück (Algorithmus-Funktionsobjekt) |
(C++20) |
gibt den größeren der gegebenen Werte zurück (Algorithmus-Funktionsobjekt) |
(C++20) |
prüft, ob ein Integerwert im Bereich eines gegebenen Integer-Typs liegt (Funktionstemplate) |
(C++17) |
begrenzt einen Wert zwischen einem Paar von Grenzwerten (Funktionstemplate) |