Teilen über


<ranges>

Auf hoher Ebene ist ein Bereich etwas, das Sie durchlaufen können. Ein Bereich wird durch einen Iterator dargestellt, der den Anfang des Bereichs und einen Sentinel markiert, der das Ende des Bereichs markiert. Der Sentinel kann der gleiche Typ wie der Begin Iterator sein, oder es kann anders sein. Die Container, z vector . B. und list, in der C++-Standardbibliothek sind Bereiche. Ein Bereich abstrahiert Iteratoren auf eine Weise, die die Verwendung der Standardvorlagenbibliothek (Standard Template Library, STL) vereinfacht und verstärkt.

STL-Algorithmen verwenden in der Regel Iteratoren, die auf den Teil der Sammlung verweisen, auf den sie arbeiten sollten. Überlegen Sie sich beispielsweise, wie Sie eine vector Sortierung mithilfe von std::sort(). Sie übergeben zwei Iteratoren, die den Anfang und das Ende der .vector Das bietet Flexibilität, aber das Übergeben der Iteratoren an den Algorithmus ist zusätzliche Arbeit, da Sie wahrscheinlich nur das Ganze sortieren möchten.

Mit Bereichen können Sie anrufen std::ranges::sort(myVector);, was wie bei Einem Anruf std::sort(myVector.begin(), myVector.end());behandelt wird. In Bereichsbibliotheken nehmen Algorithmen Bereiche als Parameter an (obwohl sie auch Iteratoren verwenden können, wenn Sie möchten). Sie können direkt mit Sammlungen arbeiten. Beispiele für Bereichsalgorithmen, die verfügbar sind<algorithm>, sind : copy, copy_n, copy_if, all_of, equalnone_offind_iffind_if_notfindany_ofcount_iffor_eachcountfor_each_nund .mismatch

Aber vielleicht ist der wichtigste Vorteil von Bereichen, dass Sie STL-Algorithmen verfassen können, die auf Bereichen in einem Stil arbeiten, der an die funktionale Programmierung erinnert.

Beispiel für Bereiche

Vor Bereichen müssen Sie, wenn Sie die Elemente einer Auflistung transformieren möchten, die ein bestimmtes Kriterium erfüllt haben, einen Zwischenschritt einführen, um die Ergebnisse zwischen Vorgängen zu halten. Wenn Sie beispielsweise einen Vektor von Quadraten aus den Elementen in einem anderen Vektor erstellen möchten, der von drei divisierbar ist, könnten Sie etwa folgendes schreiben:

std::vector<int> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
std::vector<int> intermediate, output;

std::copy_if(input.begin(), input.end(), std::back_inserter(intermediate), [](const int i) { return i%3 == 0; });
std::transform(intermediate.begin(), intermediate.end(), std::back_inserter(output), [](const int i) {return i*i; });

Mit Bereichen können Sie dasselbe erreichen, ohne dass der intermediate Vektor benötigt wird:

// requires /std:c++20
std::vector<int> input = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

auto output = input
    | std::views::filter([](const int n) {return n % 3 == 0; })
    | std::views::transform([](const int n) {return n * n; });

Abgesehen davon, dass dieser Code einfacher zu lesen ist, vermeidet dieser Code die Speicherzuordnung, die für den intermediate Vektor und dessen Inhalt erforderlich ist. Außerdem können Sie zwei Vorgänge verfassen.

Im vorherigen Code wird jedes Element, das durch drei divisierbar ist, mit einem Vorgang zum Quadratieren dieses Elements kombiniert. Das Rohrsymbol (|) verkettet die Vorgänge zusammen und wird von links nach rechts gelesen.

Das Ergebnis ist outputselbst eine Art von Bereich, der als Ansicht bezeichnet wird.

Ansichten

Eine Ansicht ist ein einfacher Bereich. Anzeigen von Vorgängen – z. B. Standardkonstruktion, Verschieben der Konstruktion/Zuordnung, Kopieren von Konstruktion/Zuordnung (sofern vorhanden), Zerstörung, Anfang und Ende –, erfolgen unabhängig von der Anzahl der Elemente in der Ansicht in konstanter Zeit.

Ansichten werden von Bereichsadaptern erstellt, die im folgenden Abschnitt erläutert werden. Weitere Informationen zu den Klassen, die verschiedene Ansichten implementieren, finden Sie unter Ansichtsklassen.

Die Darstellung der Elemente in der Ansicht hängt von dem Bereichsadapter ab, den Sie zum Erstellen der Ansicht verwenden. Im vorherigen Beispiel akzeptiert ein Bereichsadapter einen Bereich und gibt eine Ansicht der elemente zurück, die durch drei divisierbar sind. Der zugrunde liegende Bereich ist unverändert.

Ansichten sind kompositorierbar, was leistungsfähig ist. Im vorherigen Beispiel wird die Ansicht von Vektorelementen, die durch drei divisierbar sind, mit der Ansicht kombiniert, die diese Elemente quadratisch macht.

Die Elemente einer Ansicht werden lazily ausgewertet. Das heißt, die Transformationen, die Sie auf jedes Element in einer Ansicht anwenden, werden erst ausgewertet, wenn Sie nach dem Element fragen. Wenn Sie beispielsweise den folgenden Code in einem Debugger ausführen und einen Haltepunkt in die Zeilen auto divisible_by_three = ... setzen, auto square = ...sehen Sie, dass Sie den divisible_by_three Lambda-Haltepunkt drücken, da jedes Element für input die Sichtbarkeit von drei getestet wird. Der square Lambda-Haltepunkt wird erreicht, wenn die Elemente, die durch drei dividierbar sind, quadratisch sind.

// requires /std:c++20
#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 << '\n';
    }
    return 0;
}

Weitere Informationen zu Ansichten finden Sie unter <ranges> Ansichtsklassen.

Bereichsadapter

Bereichsadapter nehmen einen Bereich und erzeugen eine Ansicht. Bereichsadapter erzeugen lazily ausgewertete Ansichten. Das heißt, Sie verursachen nicht die Kosten für die Transformation jedes Elements im Bereich, um die Ansicht zu erzeugen. Sie zahlen die Kosten, um ein Element in der Ansicht zu verarbeiten, wenn Sie auf dieses Element zugreifen.

Im vorherigen Beispiel erstellt der filter Bereichsadapter eine Ansicht mit dem Namen input , die die Elemente enthält, die durch drei divisierbar sind. Der transform Bereichsadapter betrachtet elemente, die durch drei dividierbar sind, und erstellt eine Ansicht dieser Elemente quadratisch.

Bereichsadapter können miteinander verkettet werden (zusammengesetzt), was das Herzstück der Leistungsfähigkeit und Flexibilität von Bereichen ist. Das Erstellen von Bereichsadaptern ermöglicht es Ihnen, das Problem zu überwinden, dass die vorherigen STL-Algorithmen nicht einfach zu erstellen sind.

Weitere Informationen zum Erstellen von Ansichten finden Sie unter Range adaptors.

Bereichsalgorithmen

Einige Bereichsalgorithmen verwenden ein Bereichsargument. z. B. std::ranges::sort(myVector);.

Die Bereichsalgorithmen sind nahezu identisch mit den entsprechenden Iteratorpaaralgorithmen im std Namespace. Der Unterschied besteht darin, dass sie konzeptbasierte Einschränkungen aufweisen und entweder Bereichsargumente oder mehr Iterator-Sentinel-Argumentpaare akzeptieren. Sie können direkt an einem Container arbeiten und problemlos miteinander verkettet werden.

<ranges> -Funktionen

Die folgenden Funktionen werden verwendet, um Iteratoren und Sentinels für Bereiche zu erstellen und die Größe eines Bereichs abzurufen.

Funktion Beschreibung
beginC++20 Rufen Sie einen Iterator zum ersten Element im Bereich ab.
cbeginC++20 Rufen Sie einen const Iterator zum ersten Element im Bereich ab.
cendC++20 Rufen Sie den Sentinel am Ende des constqualifizierten Bereichs ab.
cdataC++20 Rufen Sie einen const Zeiger auf das erste Element im zusammenhängenden Bereich ab.
crbeginC++20 Rufen Sie einen umgekehrten const Iterator zum Anfang des Bereichs ab.
crendC++20 Rufen Sie den Sentinel am Ende der crbegin() Rückgabe ab.
dataC++20 Rufen Sie einen Zeiger auf das erste Element im zusammenhängenden Bereich ab.
emptyC++20 Ermitteln Sie, ob der Bereich leer ist.
endC++20 Rufen Sie den Sentinel am Ende des Bereichs ab.
rbeginC++20 Rufen Sie einen umgekehrten Iterator zum Anfang des Bereichs ab.
rendC++20 Rufen Sie einen umgekehrten Iterator am Ende des Bereichs zum Sentinel ab.
sizeC++20 Ruft die Größe des Bereichs als nicht signierten Wert ab.
ssizeC++20 Ruft die Größe des Bereichs als signierten Wert ab.

Weitere Informationen finden Sie unter <ranges> Funktionen.

Bereichskonzepte

Wie Sie die Elemente eines Bereichs durchlaufen, hängt vom zugrunde liegenden Iteratortyp ab. Bereiche verwenden C++-Konzepte, die angeben, welche Iterator sie unterstützen.

In C++20 bedeutet das Konzept X das Konzept Y zu verfeinern, dass alles, was Konzept Y erfüllt, auch das Konzept X erfüllt. Zum Beispiel: Auto, Bus und Lkw alle raffinieren Fahrzeuge.

Einige Bereichskonzepte spiegeln die Hierarchie der Iteratorkategorien wieder. In der folgenden Tabelle sind die Bereichskonzepte sowie die Typen von Containern aufgeführt, auf die sie angewendet werden können.

Bereichskonzept Beschreibung Unterstützte Container
std::ranges::output_range Kann vorwärts durchlaufen werden.
std::ranges::input_range Kann von Anfang bis Ende mindestens einmal durchlaufen werden. std::forward_list
std::unordered_map
std::unordered_multimap
std::unordered_set
std::unordered_multiset
basic_istream_view
std::ranges::forward_range Kann von Anfang bis Ende mehrmals durchlaufen werden. std::forward_list
std::unordered_map
std::unordered_multimap
std::unordered_set
std::unordered_multiset
std::ranges::bidirectional_range Kann mehr als einmal vorwärts und rückwärts durchlaufen werden. std::list
std::map
std::multimap
std::multiset
std::set
std::ranges::random_access_range Kann mithilfe des [] Operators auf ein beliebiges Element (in konstanter Zeit) zugreifen. std::deque
std::ranges::contiguous_range Die Elemente werden fortlaufend im Arbeitsspeicher gespeichert. std::array
std::string
std::vector

Weitere Informationen zu diesen Konzepten finden Sie in <ranges> den Konzepten .

<ranges>-Aliasvorlagen

Die folgenden Aliasvorlagen bestimmen die Typen von Iteratoren und Sentinels für einen Bereich:

Aliasvorlage Beschreibung
borrowed_iterator_tC++20 Ermitteln Sie, ob ein für einen range Bereich zurückgegebener Iterator auf einen Bereich verweist, dessen Lebensdauer beendet wurde.
borrowed_subrange_tC++20 Ermitteln Sie, ob ein für einen subrange Verweis zurückgegebener Iterator auf einen Unterbereich verweist, dessen Lebensdauer beendet wurde.
danglingC++20 Gibt an, dass der zurückgegebene Iterator eines range/subrange Iterators die Lebensdauer des range/subrange Iterators überlebt.
iterator_tC++20 Gibt den Iteratortyp des angegebenen Bereichstyps zurück.
range_difference_tC++20 Gibt den Differenztyp des Iteratortyps des angegebenen Bereichs zurück.
range_reference_tC++20 Gibt den Bezugstyp des iteratortyps des angegebenen Bereichs zurück.
range_rvalue_reference_tC++20 Gibt den Rvalue-Bezugstyp für den Iteratortyp des angegebenen Bereichs zurück. Mit anderen Worten, der Rvalue-Bezugstyp der Elemente des Bereichs.
range_size_tC++20 Gibt den Typ zurück, der zum Melden der Größe des angegebenen Bereichs verwendet wird.
range_value_tC++20 Gibt den Werttyp des Iteratortyps des angegebenen Bereichs zurück. Oder mit anderen Worten, der Typ der Elemente im Bereich.
sentinel_tC++20 Gibt den Sentineltyp des angegebenen Bereichs zurück.

Weitere Informationen zu diesen Aliasvorlagen finden Sie unter <ranges> Aliasvorlagen.

Siehe auch

<ranges> -Funktionen
<ranges> Konzepte
Bereichsadapter
Referenz zu Headerdateien