Mam kilka pytań dotyczących tego programu:
#include <iostream>
#include <type_traits>
#include <functional>
using namespace std;
template <typename T> void foo ( T x )
{
auto r=ref(x);
cout<<boolalpha;
cout<<is_same<T&,decltype(r)>::value;
}
int main()
{
int x=5;
foo (x);
return 0;
}
Wynik to:
false
Chcę wiedzieć, jeśli std::ref
nie zwraca odwołania do obiektu, to co robi? Zasadniczo jaka jest różnica między:
T x;
auto r = ref(x);
i
T x;
T &y = x;
Chcę też wiedzieć, dlaczego istnieje ta różnica? Dlaczego potrzebujemy std::ref
lub std::reference_wrapper
kiedy mamy referencje (tj. T&
)?
x = y;
w obu przypadkach?Odpowiedzi:
Well
ref
konstruuje obiekt odpowiedniegoreference_wrapper
typu, aby przechowywać odwołanie do obiektu. Co oznacza, że kiedy aplikujesz:auto r = ref(x);
Zwraca
reference_wrapper
a nie bezpośrednie odwołanie dox
(tjT&
.). Toreference_wrapper
(tj.r
) Zamiast tego utrzymujeT&
.A
reference_wrapper
jest bardzo przydatne, gdy chcesz emulować areference
obiektu, który można skopiować (można go zarówno konstruować, jak i przypisywać do kopii ).W C ++, po utworzeniu odniesienia (powiedzmy
y
) do obiektu (powiedzmyx
), toy
ix
dzielić ten sam adres bazowy . Ponadtoy
nie może odnosić się do żadnego innego przedmiotu. Nie możesz również utworzyć tablicy referencji, tj. Kod taki jak ten spowoduje błąd:#include <iostream> using namespace std; int main() { int x=5, y=7, z=8; int& arr[] {x,y,z}; // error: declaration of 'arr' as array of references return 0; }
Jednak jest to zgodne z prawem:
#include <iostream> #include <functional> // for reference_wrapper using namespace std; int main() { int x=5, y=7, z=8; reference_wrapper<int> arr[] {x,y,z}; for (auto a: arr) cout << a << " "; return 0; } /* OUTPUT: 5 7 8 */
Mówiąc o Twoim problemie
cout << is_same<T&,decltype(r)>::value;
, rozwiązaniem jest:cout << is_same<T&,decltype(r.get())>::value; // will yield true
Pokażę wam program:
#include <iostream> #include <type_traits> #include <functional> using namespace std; int main() { cout << boolalpha; int x=5, y=7; reference_wrapper<int> r=x; // or auto r = ref(x); cout << is_same<int&, decltype(r.get())>::value << "\n"; cout << (&x==&r.get()) << "\n"; r=y; cout << (&y==&r.get()) << "\n"; r.get()=70; cout << y; return 0; } /* Ouput: true true true 70 */
Tutaj poznajemy trzy rzeczy:
reference_wrapper
Przedmiotu (tutajr
) mogą być wykorzystywane do tworzenia szereg odnośników , których nie było możliweT&
.r
faktycznie działa jak prawdziwe odniesienie (zobacz, jakr.get()=70
zmieniła się wartośćy
).r
to nie to samo, coT&
aler.get()
jest. Oznacza to, żer
holdT&
ie, jak sama nazwa wskazuje, jest opakowaniem wokół referencjiT&
.Mam nadzieję, że ta odpowiedź jest więcej niż wystarczająca, aby wyjaśnić Twoje wątpliwości.
źródło
reference_wrapper
można ponownie przypisać element a , ale nie może on „utrzymywać odniesienia do więcej niż jednego obiektu”. 2/3: Słuszna uwaga na temat tego, gdzie.get()
jest właściwe - ale bez sufiksur
można użyć tak samo jakT&
w przypadkach, gdyr
konwersjaoperator
może zostać wywołana jednoznacznie - więc nie ma potrzeby wywoływania.get()
w wielu przypadkach, w tym kilku w kodzie (co jest trudne do odczytania ze względu na brak miejsca).reference_wrapper
może przechowywać tablicę odniesień, jeśli nie masz pewności, możesz spróbować samemu. Plus.get()
jest używany, gdy chcesz zmienić wartość obiektu, któryreference_wrapper
trzyma, tj.r=70
Jest nielegalny, więc musisz go użyćr.get()=70
. Spróbuj sam !!!!!!int a[4]{1, 2, 3, 4}; int (&b)[4] = a;
? reference_wrapper nie jest wyjątkowy, ponieważ tutaj rodzimyT&
wykonuje pracę.wrapper
puszka może być umieszczona w pojemniku. Jest to przydatne, ale myślę, że ludzie błędnie interpretują to jako bardziej zaawansowane niż w rzeczywistości. Jeśli chcę mieć tablicę „referencji”, zwykle pomijam pośrednikówvector<Item *>
, do którychwrapper
sprowadza się ... i mam nadzieję, że puryści przeciwni wskaźnikom mnie nie znajdą. Przekonujące przykłady użycia są różne i bardziej złożone.std::reference_wrapper
jest rozpoznawany przez standardowe narzędzia jako zdolny do przekazywania obiektów przez odwołanie w kontekstach przekazujących wartość.Na przykład,
std::bind
może przejąćstd::ref()
coś do czegoś, przesłać to według wartości, a później rozpakować z powrotem do odniesienia.void print(int i) { std::cout << i << '\n'; } int main() { int i = 10; auto f1 = std::bind(print, i); auto f2 = std::bind(print, std::ref(i)); i = 20; f1(); f2(); }
Ten fragment kodu wyświetla:
10 20
Wartość
i
została zapisana (przyjęta przez wartość) wf1
punkcie, w którym została zainicjowana, alef2
zachowałastd::reference_wrapper
wartość według, a zatem zachowuje się tak, jakby przyjęła plikint&
.źródło
std::ref(T)
zwraca plikstd::reference_wrapper
. To niewiele więcej niż owinięty wskaźnik, ale jest rozpoznawany przez bibliotekę jako „hej, mam być punktem odniesienia! Proszę, zamień mnie z powrotem w jednego, gdy skończysz mnie przekazywać”.Odniesienie (
T&
lubT&&
) to specjalny element w języku C ++. Pozwala na manipulowanie obiektem poprzez odniesienie i ma specjalne przypadki użycia w języku. Na przykład nie można utworzyć standardowego kontenera do przechowywania odniesień:vector<T&>
jest źle sformułowany i generuje błąd kompilacji.A
std::reference_wrapper
z drugiej strony jest obiektem C ++ zdolnym do przechowywania referencji. W związku z tym można go używać w standardowych pojemnikach.std::ref
jest standardową funkcją, która zwraca astd::reference_wrapper
w swoim argumencie. W tym samym pomyślestd::cref
powracastd::reference_wrapper
do odwołania do stałej.Jedną z interesujących właściwości a
std::reference_wrapper
jest to, że ma rozszerzenieoperator T& () const noexcept;
. Oznacza to, że nawet jeśli jest to prawdziwy obiekt , można go automatycznie przekształcić w odniesienie, które posiada. Więc:operator T& () const noexcept;
niemu można go używać wszędzie tam, gdzie można użyć odniesienia, ponieważ zostanie na niego automatycznie przekonwertowany.źródło
operator T& ()
których 2 pozostałych odpowiedziach nie ma.