<ranges>
-Ansichtsklassen
Eine Ansicht ist ein einfacher Bereich, der sich auf Elemente bezieht, die sie nicht besitzen (mit Ausnahme owning_view
). Eine Ansicht basiert in der Regel auf einem anderen Bereich und bietet eine andere Möglichkeit, sie zu betrachten, unabhängig davon, ob sie transformiert oder gefiltert wird. Beispielsweise ist eine Ansicht, die die Kriterien verwendet, std::views::filter
die Sie zum Auswählen von Elementen aus einem anderen Bereich angeben.
Wenn Sie auf die Elemente in einer Ansicht zugreifen, wird sie "lazily" ausgeführt, sodass die Arbeit nur ausgeführt wird, wenn Sie ein Element erhalten. Dies ermöglicht es, Ansichten ohne Leistungseinbußen zu kombinieren oder zu verfassen.
Sie können z. B. eine Ansicht erstellen, die nur die geraden Elemente aus einem Bereich bereitstellt, und sie dann durch Quadratieren transformieren. Die Arbeit zum Filtern und Transformieren erfolgt nur für die Elemente, auf die Sie zugreifen, und nur dann, wenn Sie darauf zugreifen.
Eine Ansicht kann in konstanter Zeit kopiert, zugewiesen und zerstört werden, unabhängig davon, wie viele Elemente sie enthält. Dies liegt daran, dass eine Ansicht nicht über die Elemente verfügt, auf die sie verweist, sodass sie keine Kopie erstellen müssen. Aus diesem Grund können Sie Ansichten ohne Leistungseinbußen verfassen.
In der Regel erstellen Sie eine Ansicht mithilfe eines Bereichsadapters. Range adaptors are the intended way to create a view, are easier to use than instantiating the view classes directly, and are sometimes more efficient than instantiating the view classes directly. Die Ansichtsklassen werden direkt verfügbar gemacht, falls Sie ihren eigenen benutzerdefinierten Ansichtstyp basierend auf einem vorhandenen Ansichtstyp erstellen müssen.
Im Folgenden finden Sie ein kurzes Beispiel zum Erstellen einer Ansicht der Quadrate der Elemente, die in einem Vektor durch drei divisierbar sind:
// requires /std:c++20 or later
#include <ranges>
#include <vector>
#include <iostream>
int main()
{
std::vector<int> input = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto divisible_by_three = [](const int n) {return n % 3 == 0;};
auto square = [](const int n) {return n * n;};
auto x = input
| std::views::filter(divisible_by_three)
| std::views::transform(square);
for (int i : x)
{
std::cout << i << ' '; // 0 9 36 81
}
}
0 9 36 81
Die Verwendung einer Ansicht nach der Änderung des Bereichs, auf dem sie basiert, kann zu einem nicht definierten Verhalten führen. Beispielsweise sollte ein reverse_view
auf einem Vektor basierender Vektor nicht wiederverwendet werden, wenn Sie Elemente aus dem zugrunde liegenden Vektor hinzufügen oder entfernen. Durch das Ändern des zugrunde liegenden Vektors end
wird der Iterator des Containers ungültig, einschließlich der Kopie des Iterators, den die Ansicht möglicherweise erstellt hat.
Da Ansichten günstig zu erstellen sind, sollten Sie im Allgemeinen eine Ansicht erneut erstellen, wenn Sie den zugrunde liegenden Bereich ändern. Im folgenden Beispiel wird veranschaulicht, wie eine Ansichtspipeline in einer Variablen gespeichert wird, damit Sie sie wiederverwenden können.
// requires /std:c++20, or later
#include <iostream>
#include <ranges>
#include <vector>
#include <list>
#include <string_view>
#include <algorithm>
template<typename rangeType>
void show(std::string_view msg, rangeType r)
{
std::cout << msg;
std::ranges::for_each(r,
[](auto e)
{
std::cout << e << ' ';
});
std::cout << '\n';
}
int main()
{
std::vector v{ 1, 2, 3, 4 };
show("v: ", v);
// You can save a view pipeline
auto rev3 = std::views::take(3) | std::views::reverse;
show("v | rev3: ", v | rev3); // 3 2 1
v.insert(v.begin(), 0); // v = 0 1 2 3 4
show("v: ", v);
// Because modifying the vector invalidates its iterators, rebuild the view.
// We are reusing the view pipeline we saved earlier
show("v | rev3(v): ", rev3(v));
}
v: 1 2 3 4
v | rev3: 3 2 1
v: 0 1 2 3 4
v | rev3(v): 2 1 0
Die folgenden Ansichtsklassen werden im std::ranges
Namespace definiert.
Sicht | Beschreibung |
---|---|
basic_istream_view C++20 |
Eine Ansicht der aufeinander folgenden Elemente aus einem Eingabedatenstrom. Spezialisierungen umfassen istream_view und wistream_view . |
common_view C++20 |
Passt eine Ansicht mit unterschiedlichen Iterator-/Sentineltypen in eine Ansicht mit denselben Iterator-/Sentinel-Typen an. |
drop_view C++20 |
Erstellt aus einer anderen Ansicht, wobei die ersten count Elemente übersprungen werden. |
drop_while_view C++20 |
Erstellt aus einer anderen Ansicht, wobei führende Elemente übersprungen werden, solange ein Prädikat haltet. |
elements_view C++20 |
Eine Ansicht über dem ausgewählten Index in jedem Tupel-ähnlichen Wert in einer Auflistung. Erstellen Sie beispielsweise bei einem Wertebereich std::tuple<string, int> eine Ansicht, die string aus allen Elementen aus jedem Tupel besteht. |
empty_view C++20 |
Eine Ansicht ohne Elemente. |
filter_view C++20 |
Filtert Elemente eines Bereichs aus, die nicht mit einem Prädikat übereinstimmen. |
iota_view C++20 |
Eine generierte Ansicht, die eine Folge von inkrementierenden Werten enthält. |
join_view C++20 |
Kombiniert alle Elemente mehrerer Bereiche in einer einzigen Ansicht. |
keys_view C++20 |
Eine Ansicht über dem ersten Index in jedem Tupel-ähnlichen Wert in einer Auflistung. Erstellen Sie beispielsweise bei einem Wertebereich std::tuple<string, int> eine Ansicht, die aus den string Elementen aus jedem Tupel besteht. |
lazy_split_view C++20 |
Teilt eine Ansicht basierend auf einem Trennzeichen in Unterbereiche auf. |
owning_view C++20 |
Übernimmt den Besitz der Elemente aus einem anderen Bereich. |
ref_view C++20 |
Eine Ansicht, die auf die Elemente verweist, die zu einem anderen Bereich gehören. |
reverse_view C++20 |
Stellt die Elemente eines Bereichs in umgekehrter Reihenfolge dar. |
single_view C++20 |
Eine Ansicht, die nur ein Element enthält. |
split_view C++20 |
Teilt eine Ansicht basierend auf einem Trennzeichen in Unterbereiche auf. |
subrange C++20 |
Eine Ansicht eines Teils der Elemente eines Bereichs, wie durch einen Begin Iterator und einen Sentinel definiert. |
take_view C++20 |
Enthält die angegebene Anzahl von Elementen, die von der Vorderseite eines Bereichs stammen. |
take_while_view C++20 |
Enthält die führenden Elemente eines Bereichs, die dem angegebenen Prädikat entsprechen. |
transform_view C++20 |
Eine Ansicht einer zugrunde liegenden Sequenz, nachdem eine Transformationsfunktion auf jedes Element angewendet wurde. |
values_view C++20 |
Eine Ansicht über dem zweiten Index in jedem Tupel-ähnlichen Wert in einer Auflistung. Erstellen Sie beispielsweise bei einem Wertebereich std::tuple<string, int> eine Ansicht, die aus den int Elementen aus jedem Tupel besteht. |
Viele dieser Klassen verfügen über entsprechende Bereichsadapter im std:views
Namespace, die Instanzen dieser Klassen erstellen. Verwenden Sie lieber einen Adapter, um eine Ansicht zu erstellen, anstatt Ansichtsklassen direkt zu erstellen. Die Bereichsadapter sind die beabsichtigte Möglichkeit zum Erstellen von Ansichten, sind einfacher zu verwenden und sind in einigen Fällen effizienter.
Eigenschaften von Klassen anzeigen
Jedes Ansichtsklassenthema verfügt über einen Abschnitt "Merkmale" nach dem Syntaxabschnitt. Der Abschnitt "Merkmale" enthält die folgenden Einträge:
Bereichsadapter: Eine Verknüpfung mit dem Bereichsadapter, der die Ansicht erstellt. Normalerweise verwenden Sie einen Bereichsadapter, um eine Ansicht zu erstellen, anstatt eine Ansichtsklasse direkt zu erstellen, sodass sie hier zur Vereinfachung aufgeführt ist.
Zugrunde liegender Bereich: Ansichten weisen unterschiedliche Iteratoranforderungen für die Art des zugrunde liegenden Bereichs auf, die sie verwenden können. Weitere Informationen zu den Arten von Iteratoren finden Sie in der Bereichs iteratorhierarchie .
Iteratorkategorie anzeigen: Die Iteratorkategorie der Ansicht. Wenn eine Ansicht einen Bereich anpasst, entspricht der Iteratortyp für die Ansicht in der Regel dem Iteratortyp des zugrunde liegenden Bereichs. Es kann jedoch für einige Ansichten anders sein. Hat z. B
reverse_view
. einbidirectional_iterator
, auch wenn der zugrunde liegende Bereich einenrandom_access_iterator
.Elementtyp: Der Typ der Elemente, die der Iterator der Ansicht zurückgibt.
Größe: Gibt an, ob die Ansicht die Anzahl der Elemente zurückgeben kann, auf die sie verweist. Nicht alle Ansichten können.
Allgemeiner Bereich: Gibt an, ob es sich bei der Ansicht um eine
common_range
Ansicht handelt. Dies bedeutet, dass der Anfangs iterator und die Sentinel-Typen identisch sind. Allgemeine Bereiche sind nützlich für Code vor dem Bereich, der mit Iteratorpaaren funktioniert. Ein Beispiel ist iteratorpaarkonstruktoren für einen Sequenzcontainer, zvector(ranges::begin(x), ranges::end(x))
. B. .Geliehener Bereich: Gibt an, ob es sich bei der Ansicht um einen ausgeliehenen Bereich handelt.
borrowed_range<T>
bedeutet, dass Sie Iteratoren fürT
die ZerstörungT
verwenden können.Kein Standardcontainer ist ein geliehener Bereich, da das Zerstören des Containers die Elemente freigibt und iteratoren ungültig macht. In diesem Fall sagen wir, dass die Iteratoren nach der Zerstörung "gezängt" bleiben.
Gibt beispielsweise
std::ranges::find()
in der Regel einen Iterator an das gefundene Element im Bereichsargument zurück. Wenn es sich bei dem Bereichsargument um einen temporären Container (rvalue) handelt, ist es ein Fehler, den zurückgegebenen Iterator zu speichern und später zu verwenden, da es sich um "Ngling" handelt.Bereichsalgorithmen, die Iteratoren (oder Unterbereiche) zurückgeben, tun dies nur, wenn ihre Argumente lvalues (nicht-temporär) oder ausgeliehene Bereiche sind. Andernfalls geben sie ein
std::dangling
Objekt zurück, das fehlermeldungen enthält, was schief gelaufen ist, wenn Sie versucht haben, es wie ein Iterator zu verwenden.Ist
const
iterierbar: Gibt an, ob Sie eineconst
Instanz der Ansicht durchlaufen können. Nicht alleconst
Ansichten können iteriert werden. Wenn eine Ansicht nichtconst
iterierbar ist, können Sie sie nichtfor (const auto& element : as_const(theView))
durchlaufen oder an eine Funktion übergeben, die einenconst
Verweis auf die Ansicht verwendet, und dann versucht, die Ansicht zu durchlaufen.
Iteratorhierarchie für Bereiche
Im Abschnitt "Merkmale" jedes Ansichtsklassenthemas bezieht sich die Iteratorkategorie im zugrunde liegenden Bereich und die Kategorie "Iterator anzeigen" auf die Art des Iterators, den der Bereich/die Ansicht unterstützt. Es gibt sechs Kategorien von Ranges Iteratoren, die durch C++20-Konzepte identifiziert werden. Die Hierarchie der Bereichs iteratoren, in zunehmender Reihenfolge der Funktion, lautet:
Range iterator concept | Beschreibung |
---|---|
output_range |
Schreibgeschützt, nur vorwärts; Single Pass. |
input_range |
Schreibgeschützt, nur vorwärts; Single Pass. |
forward_range |
Nur vorwärts; Multidurchlauf. |
bidirectional_range |
Kann vorwärts und rückwärts gehen; Multidurchlauf. |
random_access_range |
Kann mit einem Index auf die Sammlung zugreifen; Multidurchlauf. |
contiguous_range |
Kann auf die Auflistung mit einem Index zugreifen, und Elemente werden zusammenhängend im Speicher gespeichert. |
Im Allgemeinen verfügt ein Iterator über die Fähigkeit der Iteratoren, die ihm in der Tabelle vorangehen. Verfügt beispielsweise bidirectional_range
über die Funktionen von forward_range
, aber nicht umgekehrt. Außer input_range
, die nicht über die Funktion output_range
verfügt, weil Sie nicht in ein input_range
.
Die Anweisung "erfordert input_range
oder höher" bedeutet, dass die Ansicht mit einem input_range
, forward_range
, bidirectional_range
, , , random_access_range
oder contiguous_range
Iterator verwendet werden kann, da sie alle so fähig sind wie input_range
.
Die Iteratorhierarchie der Bereiche ist direkt mit der Iteratorhierarchie verknüpft. Weitere Informationen finden Sie unter Iterator-Konzepte.