17,5 — Tablice odniesień poprzez std::reference_wrapper

W poprzedniej lekcji wspominaliśmy, że tablice mogą zawierać elementy dowolnego typu obiektu. Dotyczy to obiektów o typach podstawowych (np. int) i obiektów o typach złożonych (np. wskaźnik do int).

#include <array>
#include <iostream>
#include <vector>

int main()
{
    int x { 1 };
    int y { 2 };

    [[maybe_unused]] std::array valarr { x, y };   // an array of int values
    [[maybe_unused]] std::vector ptrarr { &x, &y }; // a vector of int pointers
    
    return 0;
}

Ponieważ jednak referencje nie są obiektami, nie można utworzyć tablicy odniesień. Elementy tablicy muszą także umożliwiać przypisanie i nie można ponownie umieszczać odniesień.

#include <array>
#include <iostream>

int main()
{
    int x { 1 };
    int y { 2 };

    [[maybe_unused]] std::array<int&, 2> refarr { x, y }; // compile error: cannot define array of references

    int& ref1 { x };
    int& ref2 { y };
    [[maybe_unused]] std::array valarr { ref1, ref2 }; // ok: this is actually a std::array<int, 2>, not an array of references

    return 0;
}

W tej lekcji będziemy używać std::array w przykładach, ale ma to jednakowe zastosowanie do wszystkich typów tablic.

Jeśli jednak potrzebujesz tablicy odniesień, istnieje obejście.

std::reference_wrapper

std::reference_wrapper to standardowy szablon klasy bibliotecznej, który znajduje się w nagłówku <funkcjonalny>. Przyjmuje argument szablonu typu T, a następnie zachowuje się jak modyfikowalne odwołanie do wartości T.

Jest kilka rzeczy, o których warto pamiętać std::reference_wrapper:

  • Operator= ponowne osadzenie a std::reference_wrapper (zmiana obiektu, do którego się odwołuje).
  • std::reference_wrapper<T> będzie niejawnie konwertowana do T&.
  • Klasa get() funkcji składowej, której można użyć do uzyskania T&. Jest to przydatne, gdy chcemy zaktualizować wartość obiektu, do którego się odwołujemy.

Oto prosty przykład:

#include <array>
#include <functional> // for std::reference_wrapper
#include <iostream>

int main()
{
    int x { 1 };
    int y { 2 };
    int z { 3 };

    std::array<std::reference_wrapper<int>, 3> arr { x, y, z };
    
    arr[1].get() = 5; // modify the object in array element 1

    std::cout << arr[1] << y << '\n'; // show that we modified arr[1] and y, prints 55
    
    return 0;
}

Ten przykład wyświetla następujący komunikat:

55

Zauważ, że musimy użyć arr[1].get() = 5 , a nie arr[1] = 5. To drugie jest niejednoznaczne, ponieważ kompilator nie jest w stanie stwierdzić, czy zamierzamy ponownie ustawić std::reference_wrapper<int> na wartość 5 (co i tak jest nielegalne), czy też zmienić wartość, do której się odwołujemy. Użycie get() ujednoznacznia to.

Podczas drukowania arr[1] kompilator zorientuje się, że nie może wydrukować std::reference_wrapper<int>, więc niejawnie przekonwertuje go na int&, który może wydrukować. Nie musimy więc używać get() tutaj.

std::ref i std::cref

Przed C++17 nie istniało CTAD (dedukcja argumentów szablonu klasy), więc wszystkie argumenty szablonu dla typu klasy musiały być jawnie wymienione. Zatem, aby utworzyć std::reference_wrapper<int>, można wykonać jedną z poniższych czynności:

    int x { 5 };

    std::reference_wrapper<int> ref1 { x };        // C++11
    auto ref2 { std::reference_wrapper<int>{ x }}; // C++11

Pomiędzy długą nazwą a koniecznością jawnego wyszczególnienia argumentów szablonu, utworzenie wielu takich opakowań referencyjnych może być uciążliwe.

Aby było łatwiej, std::ref() i std::cref() funkcje zostały udostępnione jako skróty do tworzenia std::reference_wrapper i const std::reference_wrapper obiektów opakowanych. Należy zauważyć, że tych funkcji można używać z auto aby uniknąć konieczności jawnego określania argumentu szablonu.

    int x { 5 };
    auto ref { std::ref(x) };   // C++11, deduces to std::reference_wrapper<int>
    auto cref { std::cref(x) }; // C++11, deduces to std::reference_wrapper<const int>

Oczywiście teraz, gdy mamy CTAD w C++17, możemy również to zrobić:

    std::reference_wrapper ref1 { x };        // C++17
    auto ref2 { std::reference_wrapper{ x }}; // C++17

Ale ponieważ std::ref() i std::cref() są krótsze w pisaniu, nadal są powszechnie używane utwórz std::reference_wrapper obiekty.

guest
Twój adres e-mail nie zostanie wyświetlony
Znalazłeś błąd? Zostaw komentarz powyżej!
Komentarze związane z poprawkami zostaną usunięte po przetworzeniu, aby pomóc zmniejszyć bałagan. Dziękujemy za pomoc w ulepszaniu witryny dla wszystkich!
Awatary z https://gravatar.com/ są połączone z podanym adresem e-mail.
Powiadamiaj mnie o odpowiedziach:  
39 Komentarze
Najnowsze
Najstarsze Najczęściej głosowane
Wbudowane opinie
Wyświetl wszystkie komentarze