Megosztás a következőn keresztül:


<ranges> osztályok megtekintése

A nézet egy egyszerűsített tartomány, amely olyan elemekre hivatkozik, amelyek nem birtokolják (kivéve owning_view). A nézetek általában egy másik tartományon alapulnak, és másképpen tekintik meg, akár átalakítással, akár szűréssel. Ez a nézet például std::views::filter a megadott feltételek alapján választ ki elemeket egy másik tartományból.

Amikor egy nézetben hozzáfér az elemekhez, az "lustán" történik, így a munka csak akkor végezhető el, ha egy elemet kap. Ez lehetővé teszi a nézetek kombinálását vagy összeállítását teljesítménybüntetés nélkül.

Létrehozhat például egy nézetet, amely csak a tartomány páros elemeit biztosítja, majd azokat guggolással átalakítja. A szűrést és átalakítást csak az Ön által elért elemeken kell elvégezni, és csak akkor, ha ön hozzáfér hozzájuk.

A nézeteket állandó időben lehet másolni, hozzárendelni és megsemmisíteni, függetlenül attól, hogy hány elemet tartalmaz. Ennek az az oka, hogy egy nézet nem rendelkezik az általa hivatkozott elemekkel, ezért nem kell másolatot készítenie. Ezért lehet teljesítménybüntetés nélkül nézeteket írni.

A nézetet általában tartományadarelátor használatával hozza létre. A tartományadaposítók célja egy nézet létrehozása, használata egyszerűbb, mint a nézetosztályok közvetlen példányosítása, és néha hatékonyabbak, mint a nézetosztályok közvetlen példányosítása. A nézetosztályok közvetlenül akkor jelennek meg, ha saját egyéni nézettípust kell létrehoznia egy meglévő nézettípus alapján.

Íme egy rövid példa a vektorban háromval osztható elemek négyzeteinek nézetére:

// 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

Ha egy nézetet a módosított tartomány után használ, az meghatározatlan viselkedéshez vezethet. Ha például elemeket ad hozzá vagy távolít el a mögöttes vektorból, reverse_view nem szabad újra felhasználni egy vektoron alapulót. A mögöttes vektor módosítása érvényteleníti a tároló iterátorát end – beleértve annak az iterátornak a másolatát is, amelyet a nézet esetleg készített.

Mivel a nézetek létrehozása olcsó, általában újra létre kell hoznia egy nézetet, ha módosítja az alapul szolgáló tartományt. Az alábbi példa bemutatja, hogyan tárolhat egy nézetfolyamatot egy változóban, hogy újra felhasználható legyen.

// 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

A névtérben az std::ranges alábbi nézetosztályok vannak definiálva.

Megtekintés Leírás
basic_istream_view C++20 Egy bemeneti adatfolyam egymást követő elemeinek nézete. Specializációk közé tartozik istream_view , és wistream_view.
common_view C++20 A különböző iterátor-/sentinel-típusokat tartalmazó nézeteket ugyanazon iterátor-/sentinel-típusok nézetben alakítja át.
drop_view C++20 Egy másik nézetből jött létre, kihagyva az első count elemeket.
drop_while_view C++20 Egy másik nézetből jön létre, és kihagyja a kezdő elemeket, amíg egy predikátum tart.
elements_view C++20 A kijelölt indexre mutató nézet a gyűjtemény minden rekordszerű értékére. Ha például értéktartományt std::tuple<string, int> ad meg, hozzon létre egy nézetet, amely az string egyes elemek összes eleméből áll.
empty_view C++20 Elem nélküli nézet.
filter_view C++20 Kiszűri a tartomány azon elemeit, amelyek nem felelnek meg egy predikátumnak.
iota_view C++20 Generált nézet, amely növekményes értékek sorozatát tartalmazza.
join_view C++20 Több tartomány összes elemét egyetlen nézetbe egyesíti.
keys_view C++20 Az első indexre mutató nézet a gyűjtemény minden rekordszerű értékére vonatkozóan. Ha például értéktartományt std::tuple<string, int> ad meg, hozzon létre egy nézetet, amely az string egyes elemekből áll.
lazy_split_view C++20 Elválasztó alapján feloszt egy nézetet alrangokra.
owning_view C++20 Átveszi az elemek tulajdonjogát egy másik tartományból.
ref_view C++20 Egy nézet, amely egy másik tartományhoz tartozó elemekre hivatkozik.
reverse_view C++20 Egy tartomány elemeit fordított sorrendben jeleníti meg.
single_view C++20 Csak egy elemet tartalmazó nézet.
split_view C++20 Elválasztó alapján feloszt egy nézetet alrangokra.
subrange C++20 Egy tartomány elemeinek egy részének nézete, amelyet egy kezdő iterátor és egy sentinel határoz meg.
take_view C++20 A tartomány elejéről vett elemek megadott számát tartalmazza.
take_while_view C++20 Az adott predikátumnak megfelelő tartomány első elemeit tartalmazza.
transform_view C++20 Egy mögöttes sorozat nézete az egyes elemekre egy átalakítási függvény alkalmazása után.
values_view C++20 A második indexre mutató nézet a gyűjtemény minden rekordszerű értékére. Ha például értéktartományt std::tuple<string, int> ad meg, hozzon létre egy nézetet, amely az int egyes elemekből áll.

Ezen osztályok közül sok rendelkezik megfelelő tartomány-adapterrel a std::views névtérben, amely példányokat hoz létre. Inkább használjon adaptert a nézet létrehozásához ahelyett, hogy közvetlenül hoz létre nézetosztályokat. A tartományok adapterei a nézetek létrehozásának, egyszerűbb használatának és bizonyos esetekben hatékonyabbaknak a célja.

Osztályok jellemzőinek megtekintése

Minden nézetosztály témaköre rendelkezik egy Jellemzők szakaszsal a szintaxis szakasz után. A Jellemzők szakasz a következő bejegyzéseket tartalmazza:

  • Tartományadafótor: A nézetet létrehozó tartományadapultra mutató hivatkozás. A nézetosztályok közvetlen létrehozása helyett általában tartományadalátor használatával hoz létre nézetet, így az itt látható a kényelem érdekében.

  • Alapul szolgáló tartomány: A nézetek különböző iterátorkövetelményeket támasztanak a használható mögöttes tartomány típusához. Az iterátorok típusával kapcsolatos további információkért tekintse meg a tartományok iterátorhierarchiáját .

  • Iterátor kategória megtekintése: A nézet iterátor kategóriája. Ha egy nézet módosít egy tartományt, a nézet iterátortípusa általában megegyezik az alapul szolgáló tartomány iterátortípusával. Egyes nézetek esetében azonban eltérő lehet. Például reverse_view van egy bidirectional_iterator, még akkor is, ha az alapul szolgáló tartomány rendelkezik egy random_access_iterator.

  • Elem típusa: A nézet iterátora által visszaadott elemek típusa.

  • Méret: Azt jelzi, hogy a nézet visszaadja-e az általa hivatkozott elemek számát. Nem minden nézetet lehet.

  • Gyakori tartomány: Meghatározza, hogy a nézet egy common_range, ami azt jelenti, hogy a kezdő iterátor és a sentinel típusok megegyeznek. A gyakori tartományok az iterátorpárokkal működő tartomány előtti kód esetében hasznosak. Ilyenek például az iterátorpárok konstruktorai egy szekvenciatárolóhoz, például vector(ranges::begin(x), ranges::end(x)).

  • Kölcsönzött tartomány: Meghatározza, hogy a nézet egy kölcsönzött tartomány-e. borrowed_range<T> azt jelenti, hogy iterátorokat T használhat a megsemmisítés után T .

    Egyetlen standard tároló sem kölcsönözhető tartomány, mert a tároló megsemmisítése felszabadítja az elemeket, és érvényteleníti az iterátorokat. Ebben az esetben azt mondjuk, hogy az iterátorok a pusztítás után "dangling" maradnak.

    Általában például std::ranges::find() egy iterátort ad vissza a tartományargumentum talált eleméhez. Ha a tartományargumentum ideiglenes (rvalue) tároló, hiba a visszaadott iterátor tárolása és későbbi használata, mert "dangling".

    Az iterátorokat (vagy alrangokat) visszaadó tartomány-algoritmusok csak akkor teszik ezt, ha argumentumaik értékértékek (nem ideiglenesek) vagy kölcsönzött tartományok. Ellenkező esetben egy std::dangling objektumot adnak vissza, amely a hibaüzenetekben arra utal, hogy mi történt, ha iterátorként próbálta használni.

  • Iterálhatóconst: Azt jelzi, hogy át tudja-e iterálni a nézet egy const példányát. Nem minden const nézetet lehet iterated. Ha egy nézet nem const iterálható, nem tudja iterálni for (const auto& element : as_const(theView)) vagy átadni egy olyan függvénynek, amely const hivatkozik a nézetre, majd megpróbálja iterálni.

Tartományok iterátorhierarchiája

Az egyes nézetosztály-témakörök Jellemzők szakaszában az alapul szolgáló tartomány és a Nézet iterátor kategóriájában az iterátor azt az iterátortípust jelöli, amelyet a tartomány/nézet támogat. A Ranges iterátorainak hat kategóriája van, amelyeket a C++20 fogalmak azonosítanak. A tartomány-iterátorok hierarchiája a képességek növekvő sorrendjében a következő:

A tartomány iterátorának fogalma Leírás
output_range Írásvédett, csak előrehalad; egyirányú.
input_range Írásvédett, csak előrehalad; egyirányú.
forward_range Csak előrehalad; több-pass.
bidirectional_range Előre és hátra is mozoghat; több-pass.
random_access_range Hozzáférhet a gyűjteményhez egy index használatával; több-pass.
contiguous_range Hozzáférhet a gyűjteményhez egy index segítségével, és az elemeket a rendszer egyidejűleg tárolja a memóriában.

Általánosságban elmondható, hogy az iterátor képes az azt megelőző iterátorokra a táblázatban. Például rendelkezik a képességeivelbidirectional_range, forward_range de nem fordítva. Kivéve input_range, amely nem rendelkezik a képesség, output_range mert nem tud írni egy input_range.

A "szükséges input_range vagy magasabb" utasítás azt jelenti, hogy a nézet használható egy input_range, forward_range, , bidirectional_rangerandom_access_range, vagy contiguous_range iterátorsal, mert mind olyan alkalmas, mint input_range.

A tartományok iterátorhierarchiája közvetlenül kapcsolódik az iterátorhierarchiához. További információkért tekintse meg az Iterator alapelveit.

Lásd még

<ranges>
Távolsági adapterek