2012-03-24 13 views
6

Zastanawiam się, w jakich przypadkach sensowne jest stosowanie semantyki ruchu podczas przeciążania operatora + i/lub operatora + =. Chociaż jest to wyjaśnione w this question, jak można to zrobić, nie mogę się skupić na tym, dlaczego to zrobiłem. Rozważmy operator + =. Jeśli po prostu przekazuję prawą stronę przez odniesienie i dokonuję odpowiednich zmian na obiekcie po lewej stronie, i tak nie ma niepotrzebnych kopii. Więc wracamy do tego samego punktu: czy poruszenie semantyką byłoby w takim przypadku korzystne?Czy ma sens stosowanie semantyki przeniesienia dla operatora + i/lub operatora + =?

+0

Generalnie zły pomysł na przeciążenie operatorów. Ponieważ: ** 1) ** jeśli oczywiście jest to dobry przypadek, prawdopodobnie jest to biblioteka już dla ciebie. ** 2) ** jeśli nie jest to oczywiście dobry przypadek, to prawdopodobnie bardzo źle to zrobić. – enobayram

+3

@enobayram sprawiają, że jest to funkcja "dopisz", jeśli jesteś chybiony wokół przeciążonych operatorów. Teraz wciąż możesz odpowiedzieć na pytanie. –

+7

@obobayram, to nie ma żadnego sensu. Zgodnie z tą logiką, generalnie złym pomysłem jest pisanie kodu kiedykolwiek. – TonyK

Odpowiedz

12

Tak i nie.

operator semantyka + =

ruch nie zawsze są pomocne dla operator+= w ogóle, bo jesteś już modyfikując argument po lewej stronie (this), więc masz już środków do pracy z najbardziej czasów.

Mimo to optymalizacja może być tego warta. Wyobraź sobie implementację std::string, której domyślny konstruktor nie alokuje żadnej pamięci. Wtedy std::string::operator+=(std::string&&) może po prostu ukraść zasoby z RHS. Albo wyobraź sobie, że bufor RHS jest wystarczająco duży, aby pomieścić wszystko, ale LHS nie jest, wtedy jeśli możesz użyć bufora RHS, to jesteś złoty: po prostu zamień i wstań.

Może warto, ale trzeba się uczyć. Dlatego:

  • T& T::operator+=(T const&): zawsze obecny
  • T& T::operator+=(T&&): umożliwienie semantykę przesunięta, gdy ma to sens

operator +

Tutaj zawsze jest przydatna (pod warunkiem, że mówimy o klasach dla których przydatne są semantyki ruchu).

Rzecz polega na tym, że operator+ produkuje tymczasowo (na niebiesko), więc generalnie musi utworzyć zasoby dla tego tymczasowego. Jeśli jednak uda im się je ukraść, a nie tworzyć, jest to z pewnością tańsze.

Jednak nie trzeba dostarczyć wszystkie przeciążeń:

  • T operator+(T const&, T const&)
  • T operator+(T&&, T const&)
  • T operator+(T const&, T&&)
  • T operator+(T&&, T&&) (wymagana ujednoznacznienie)

Nie można ponownie wykorzystać ten sam podstęp, który operator= użyj i twórz tymczasowe prawo w podpisie funkcji (przez wzięcie jednego argumentu za pomocą kopii). Jeśli typ jest ruchomy, konstruktor ruchu zostanie wywołany, w przeciwnym razie będzie to konstruktor kopiowania, ale ponieważ i tak potrzebujesz tymczasowego, bez utraty wydajności.

inline T operator+(T left, T const& right) { left += right; return left; } 
inline T operator+(T const& left, T right) { right += left; return right; } // commutative 
inline T operator+(T left, T&& right) { left += right; return left; } // disambiguation 

Nie wiele zyskać (3 zamiast 4), ale cóż, wezmę co mogę!

Oczywiście dla napisu, operator+ nie jest przemienny (dlatego jest to złe przeciążenie), więc rzeczywista implementacja drugiego przeciążenia wymagałaby metody prepend.

EDIT: po Move semantics and operator overloading wydaje się, że byłem nieco zbyt entuzjastyczny. Kradzież z odpowiedzią na Ben Voigt, otrzymujemy:

inline T operator+(T left, T const& right) { left += right; return left; } 
inline T operator+(const T& left, T&& right) { right += left; return right; } 

Z drugiej strony, wydaje się to tylko pracować dla operacji przemiennych; - nie działa w ten sposób, ale prawdopodobnie można go zaadaptować, / i % z drugiej strony ...

+0

Lepiej rób 'right + = left; return right; 'sprawić, że po powrocie rozważy" right "jako wartość r. –

+0

@ JohannesSchaub-litb: Czy mogę przypuszczać, że mówisz o ostatnim przypadku? Zastanawiałem się też nad tym i czy warto przeciążać 'T && T :: operator +() i &'. –

+0

Mówię o wszystkich trzech przypadkach. Jeśli 'T' ma konstruktor ruchu, możesz go użyć, zwracając odpowiednio" left "lub" right ". Tak jak jest teraz z twoim kodem, jeśli 'operator + =' zwraca 'T &', zawsze skopiujesz parametr do wartości zwracanej zamiast go przenosić. –

1

Jeśli dodajesz dwa ciągi, wektory itd., Których nie możesz "przenieść", nie ma to sensu. Ale jeśli dodajesz, mówisz o połączonych listach, gdzie dołączanie listy jest prawdopodobnie operatorem O (1), jeśli chcesz poświęcić prawą stronę, to ma to sens.

Powiązane problemy