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 astd::reference_wrapper(zmiana obiektu, do którego się odwołuje).std::reference_wrapper<T>będzie niejawnie konwertowana doT&.- Klasa
get()funkcji składowej, której można użyć do uzyskaniaT&. 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++11Pomię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++17Ale ponieważ std::ref() i std::cref() są krótsze w pisaniu, nadal są powszechnie używane utwórz std::reference_wrapper obiekty.

