Dlaczego operator strzałki w C ++ nie jest tylko aliasem *.?
18
W c ++ operator * może być przeciążony, na przykład iteratorem, ale operator strzałki (->) (. *) Nie działa z klasami, które przeciążają operatora *. Wyobrażam sobie, że preprocesor mógłby z łatwością zastąpić wszystkie wystąpienia -> znakiem (* left) .right, i dzięki temu iteratory byłyby łatwiejsze do zaimplementowania. czy istnieje praktyczny powód, dla którego -> jest inny, czy jest to po prostu specyfika języka / projektantów?
Zasada, która foo->barjest równa, (*foo).bardotyczy tylko wbudowanych operatorów.
Unary operator *nie zawsze ma semantykę dereferencji wskaźnika. Mógłbym stworzyć bibliotekę, w której oznacza to transpozycję macierzy, zero lub więcej dopasowań parsera lub cokolwiek innego.
Sprawiłoby to, że język byłby bardziej kłopotliwy, gdyby coś, co przeciąża jednoargumentowo operator *, nagle zyskałoby coś, o operator ->co nie prosiłeś, z semantyką, która może nie mieć sensu.
operator -> jest osobno przeciążalny, więc jeśli chcesz, możesz przeciążyć jeden przy minimalnym wysiłku.
Zauważ też, że takie przeciążenie miałoby pewne dość interesujące właściwości, takie jak automatyczne łączenie operator ->wywołań, dopóki jeden w łańcuchu nie zwróci surowego wskaźnika. Jest to bardzo przydatne w przypadku inteligentnych wskaźników i innych typów proxy.
Co ilustruje twój przykład? Czy zwracasz inteligentny wskaźnik do łańcucha i w jakiś sposób wyświetlasz rozmiar? Jestem zmieszany.
Trevor Hickey,
2
Ilustruje ostatni akapit mojej odpowiedzi, w jaki sposób korzystanie z ->łańcuchów operatora, dopóki nie uzyska surowego wskaźnika do czegoś, dereferencje i uzyskiwanie dostępu do członka. Jeśli operator -> nie utworzył łańcucha, przykład byłby źle sformułowany, ponieważ share_ptr nie jest surowym wskaźnikiem.
Lars Viklund,
@ LarsViklund: Twoja odpowiedź ma problem: powiedziałeś „operator-> ... automatycznie łączy operatora-> połączenia, dopóki jeden w łańcuchu nie zwróci surowego wskaźnika”. To nie jest poprawne - użycie A->Błańcuchów składniowych co najwyżej 1 dodatkowe wywołanie. To, co faktycznie robi składnia binarna C ++ ->, nie wywołuje opeartor->bezpośrednio obiektu - zamiast tego sprawdza typ Ai sprawdza, czy jest to surowy wskaźnik. Jeśli tak, to ->usuwa go z pamięci i wykonuje Bna nim, w przeciwnym razie wywołuje obiekt operator->, usuwa z pamięci wynik (albo używając natywnego wskaźnika raw, albo innego, operator->a następnie wykonuje Bna wyniku
Guss 14.04.13
@Guss: Nie mogę znaleźć żadnego rozdziału i wersetu dla twojego roszczenia ani odtworzyć go w kompilatorze. C ++ 11 13.5.6 / 1 wskazuje, że jeżeli istnieje odpowiednie przeciążenie, x->mnależy je interpretować jako (x.operator->())->m. Jeśli LHS jest czymś, co operator->ponownie ma odpowiednie przeciążenie , proces ten powtarza się, dopóki nie pojawi się zwykły (*x).mefekt 5.2.5 / 2.
Lars Viklund,
8
„Język programowania C ++” opisuje fakt, że operatory te są różne, aby mogły być, ale także mówi:
Jeśli podasz więcej niż jeden z tych operatorów, rozsądne może być podanie równoważności, tak jak rozsądnie jest zapewnić ++xi x+=1mieć taki sam efekt jak x=x+1dla prostej zmiennejx jakiejś klasy, jeśli ++, + =, = i + są zapewnione.
Wydaje się więc, że projektanci językowe przewidziane osobne punkty przeciążenia, ponieważ może chce przeładowywać je inaczej, niż przy założeniu, że zawsze chce im się tym samym.
Zasadniczo C ++ jest zaprojektowany tak, aby sprzyjał elastyczności, więc przeciążenia *i ->są osobne. Chociaż jest to dość niezwykłe, jeśli chcesz tego wystarczająco mocno, możesz napisać te przeciążenia, aby robić zupełnie różne rzeczy (np. Może mieć sens dla języka specyficznego dla domeny zaimplementowanego w C ++).
To powiedziawszy, iteratory zrobić obsługują użycie. W starożytnych implementacjach możesz znaleźć bibliotekę, która wymaga (*iter).whateverzamiast tego iter->whatever, ale jeśli tak, jest to błąd w implementacji, a nie cecha charakterystyczna języka. Biorąc pod uwagę ilość pracy związanej z implementacją wszystkich standardowych kontenerów / algorytmów / iteratorów, nie jest zaskakujące, że niektóre wczesne wydania były nieco niekompletne, ale tak naprawdę nigdy nie były przeznaczone.
->
łańcuchów operatora, dopóki nie uzyska surowego wskaźnika do czegoś, dereferencje i uzyskiwanie dostępu do członka. Jeśli operator -> nie utworzył łańcucha, przykład byłby źle sformułowany, ponieważ share_ptr nie jest surowym wskaźnikiem.A->B
łańcuchów składniowych co najwyżej 1 dodatkowe wywołanie. To, co faktycznie robi składnia binarna C ++ ->, nie wywołujeopeartor->
bezpośrednio obiektu - zamiast tego sprawdza typA
i sprawdza, czy jest to surowy wskaźnik. Jeśli tak, to->
usuwa go z pamięci i wykonujeB
na nim, w przeciwnym razie wywołuje obiektoperator->
, usuwa z pamięci wynik (albo używając natywnego wskaźnika raw, albo innego,operator->
a następnie wykonujeB
na wynikux->m
należy je interpretować jako(x.operator->())->m
. Jeśli LHS jest czymś, cooperator->
ponownie ma odpowiednie przeciążenie , proces ten powtarza się, dopóki nie pojawi się zwykły(*x).m
efekt 5.2.5 / 2.„Język programowania C ++” opisuje fakt, że operatory te są różne, aby mogły być, ale także mówi:
Wydaje się więc, że projektanci językowe przewidziane osobne punkty przeciążenia, ponieważ może chce przeładowywać je inaczej, niż przy założeniu, że zawsze chce im się tym samym.
źródło
Zasadniczo C ++ jest zaprojektowany tak, aby sprzyjał elastyczności, więc przeciążenia
*
i->
są osobne. Chociaż jest to dość niezwykłe, jeśli chcesz tego wystarczająco mocno, możesz napisać te przeciążenia, aby robić zupełnie różne rzeczy (np. Może mieć sens dla języka specyficznego dla domeny zaimplementowanego w C ++).To powiedziawszy, iteratory zrobić obsługują użycie. W starożytnych implementacjach możesz znaleźć bibliotekę, która wymaga
(*iter).whatever
zamiast tegoiter->whatever
, ale jeśli tak, jest to błąd w implementacji, a nie cecha charakterystyczna języka. Biorąc pod uwagę ilość pracy związanej z implementacją wszystkich standardowych kontenerów / algorytmów / iteratorów, nie jest zaskakujące, że niektóre wczesne wydania były nieco niekompletne, ale tak naprawdę nigdy nie były przeznaczone.źródło
(*i).m
jest poprawny, obsługiwałi->m
tę samą semantykę.