Dlaczego jest taka potrzeba std::reference_wrapper
? Gdzie powinien być używany? Czym różni się od prostego wskaźnika? Jak jego wydajność wypada w porównaniu z prostym wskaźnikiem?
100
Dlaczego jest taka potrzeba std::reference_wrapper
? Gdzie powinien być używany? Czym różni się od prostego wskaźnika? Jak jego wydajność wypada w porównaniu z prostym wskaźnikiem?
.
zamiast->
.
nie działa tak, jak sugerujesz (chyba że w którymś momencie propozycja kropki operatora zostanie przyjęta i zintegrowana :))get()
funkcją składową lub z jej niejawną konwersją z powrotem do typu podstawowego.Odpowiedzi:
std::reference_wrapper
jest przydatna w połączeniu z szablonami. Zawija obiekt, przechowując do niego wskaźnik, umożliwiając ponowne przypisanie i kopiowanie, naśladując jego zwykłą semantykę. Nakazuje również niektórym szablonom bibliotek przechowywanie odniesień zamiast obiektów.Rozważmy algorytmy w STL, które kopiują funktory: Możesz uniknąć tej kopii, przekazując po prostu opakowanie referencyjne odnoszące się do funktora zamiast do samego funktora:
To działa, ponieważ…
…
reference_wrapper
S przeciążenie,operator()
więc można je wywołać tak jak obiekty funkcji, do których się odnoszą:… (Nie) jak zwykłe odniesienia, kopiowanie (i przypisywanie)
reference_wrappers
po prostu przypisuje punkt.Kopiowanie opakowania referencyjnego jest praktycznie równoważne kopiowaniu wskaźnika, które jest tak tanie, jak to tylko możliwe. Wszystkie wywołania funkcji nieodłącznie związane z jej używaniem (np. Te do
operator()
) powinny być po prostu wstawiane, ponieważ są jednowierszowe.reference_wrapper
s są tworzone przezstd::ref
istd::cref
:Argument szablonu określa typ i kwalifikację cv obiektu, do którego się odwołuje;
r2
odnosi się do aconst int
i zwróci tylko odniesienie doconst int
. Wywołania opakowań referencyjnych zconst
funktorami będą wywoływać tylkoconst
funkcjeoperator()
składowe s.Inicjatory Rvalue są niedozwolone, ponieważ pozwolenie im przyniosłoby więcej szkody niż pożytku. Ponieważ rvalues i tak zostałyby przeniesione (i przy gwarantowanej eliminacji kopiowania, nawet jeśli jest to częściowo unikane), nie poprawiamy semantyki; możemy jednak wprowadzić wiszące wskaźniki, ponieważ opakowanie odniesienia nie przedłuża jego życia.
Interakcja biblioteki
Jak wspomniano wcześniej, można poinstruować,
make_tuple
aby zapisać odwołanie w wynikutuple
, przekazując odpowiedni argument przezreference_wrapper
:Zauważ, że to nieco różni się od
forward_as_tuple
: Tutaj rwartości jako argumenty nie są dozwolone.std::bind
pokazuje to samo zachowanie: nie skopiuje argumentu, ale zapisze odniesienie, jeśli jest to plikreference_wrapper
. Przydatne, jeśli ten argument (lub funktor!) Nie musi być kopiowany, ale pozostaje w zakresie, gdybind
używany jest -functor.Różnica w stosunku do zwykłych wskaźników
Nie ma dodatkowego poziomu pośrednictwa syntaktycznego. Wskaźniki muszą zostać wyłuskane, aby uzyskać lwartość dla obiektu, do którego się odnoszą;
reference_wrapper
s mają niejawny operator konwersji i mogą być wywoływane jak zawijany obiekt.reference_wrapper
s, w przeciwieństwie do wskaźników, nie mają stanu zerowego. Muszą zostać zainicjowane za pomocą odwołania lub innegoreference_wrapper
.Podobieństwo występuje w semantyce płytkiej kopii: wskaźniki i
reference_wrapper
s można ponownie przypisać.źródło
std::make_tuple(std::ref(i));
lepszy odstd::make_tuple(&i);
?i
, a nie odniesienie do niego.Istnieją co najmniej dwa motywujące cele
std::reference_wrapper<T>
:Ma na celu nadanie semantyki referencyjnej obiektom przekazywanym jako parametr wartości do szablonów funkcji. Na przykład, możesz mieć duży obiekt funkcji, który chcesz przekazać,
std::for_each()
który przyjmuje parametr obiektu funkcji według wartości. Aby uniknąć kopiowania obiektu, możesz użyćPrzekazywanie argumentów jak
std::reference_wrapper<T>
dostd::bind()
wyrażenia jest dość powszechne, aby związać argumentów przez odniesienie zamiast przez wartość.Kiedy używasz an
std::reference_wrapper<T>
zstd::make_tuple()
odpowiednim elementem krotki staje sięT&
a zamiast aT
:źródło
fun
jest to obiekt funkcji (tj. obiekt klasy z operatorem wywołania funkcji), a nie funkcja: jeśli okażefun
się, że jest to rzeczywista funkcja,std::ref(fun)
nie ma celu i powoduje, że kod jest potencjalnie wolniejszy.Inną różnicą, jeśli chodzi o
reference_wrapper
samodokumentujący się kod, jest to, że użycie elementu zasadniczo zrzeka się własności obiektu. W przeciwieństwie do tego,unique_ptr
stwierdza własność, podczas gdy czysty wskaźnik może być własnością lub nie (nie można tego wiedzieć bez spojrzenia na wiele powiązanego kodu):źródło
reference_wrapper
jest lepszy od surowych wskaźników nie tylko dlatego, że jest oczywiste, że nie jest właścicielem, ale także dlatego, że nie może byćnullptr
(bez oszustw), a zatem użytkownicy wiedzą, że nie mogą przejśćnullptr
(bez oszustw) i wiesz, że nie musisz sprawdź to.Można o tym myśleć jako o wygodnej opasce wokół odniesień, dzięki czemu można ich używać w kontenerach.
W zasadzie jest to
CopyAssignable
wersjaT&
. Zawsze, gdy potrzebujesz odniesienia, ale musi być przypisane, użyjstd::reference_wrapper<T>
lub jego funkcji pomocniczejstd::ref()
. Lub użyj wskaźnika.Inne dziwactwa
sizeof
::I porównanie:
źródło
reference_wrapper
kod, czyniąc go identycznym z kodem, który używa wskaźnika lub referencji.std::reference_wrapper
ma gwarancję, że obiekt nigdy nie jest pusty. Rozważmy członka klasystd::vector<T *>
. Musisz zbadać cały kod klasy, aby zobaczyć, czy ten obiekt może kiedykolwiek przechowywać anullptr
w wektorze, podczas gdy zstd::reference_wrapper<T>
gwarancją, że masz prawidłowe obiekty.