2014-09-19 21 views
5

Czy dobrą praktyką powszechną jest zawsze wdrażanie moich operatorów przydziału przypisania przy użyciu numeru std::swap? Rozumiem, że jest to sposób na udostępnienie implementacji konstruktora kopii. Chciałbym uniknąć duplikowania samej logiki kopiowania. Więc tutaj jest to, co bym zrobił:Czy operator kopiowania powinien generalnie korzystać ze std :: swap?

class Foo 
{ 
public: 
    Foo(Foo const& other) { /* assume valid implementation */ } 

    Foo& operator= (Foo other) 
    { 
     std::swap(*this, other); 
     return *this; 
    } 
}; 

Akt przejściu „inne” do preform operator przypisania skopiować konstrukcję (mamy wspólną logikę kopiowania w tym momencie). Zakładam, że zamiana spowoduje wywołanie konstrukcji przeniesienia (która ma tutaj implementację wygenerowaną przez kompilator).

Robię to praktycznie dla każdej klasy, w której implementuję budowę kopii, ponieważ konstruktor & nigdy nie ma innych implementacji.

+0

Tak, kopiowanie i zamiana idiomu jest dobre dla każdej klasy posiadającej zasoby. Kolejną korzyścią jest silna gwarancja wyjątku, którą otrzymujesz z wymiany.Bycie "ogólną zasadą" dla bazy kodu, jeśli bardziej opartej na opiniach, imo. – quantdev

+2

Powinieneś używać 'use namespace std;' before 'swap' i usuń' std :: '. W ten sposób używa się bardziej specyficznego 'swap' jeśli jest dostępny. Poza tym jest to dość standardowe. – nwp

+2

Niektórzy twierdzą, że implementacja funkcji specjalnych członków w kategoriach 'swap' nie jest optymalna. Istnieje kilka niepotrzebnych przydziałów, ponieważ 'swap' wprowadza' inny' do określonego stanu. Również operatorzy przydzielający, którzy zapewniają silną gwarancję wyjątku, mogą być pesymizacją: użytkownik może nie potrzebować tej gwarancji, ale w każdym przypadku zapłaci za nią. – dyp

Odpowiedz

10

Twój kod nie ma konstruktora ruchu. Twój konstruktor kopiowania blokuje automatyczne tworzenie konstruktora ruchu, a próba przeniesienia klasy zamiast go kopiuje.

W przypadku przypisania ruchu Twój operator= blokuje również jego automatyczne wdrażanie i może być używany w tym miejscu.

Końcowy wynik to an infinite recursive call of = (live code).

Jeśli zastosujesz się do zasady 0, nie potrzebujesz ani konstruktora kopiowania, konstruktora przenoszenia, przeniesienia, kopiowania ani destruktora. Jeśli napiszesz któryś z nich, powinieneś być przygotowany do napisania ich wszystkich.

Korzystanie z może być użyteczne, ale ponieważ musisz napisać swój ruch-przypisać i przenieść-konstruować, robiąc albo w terminach std::swap jest nieskończoną rekurencją czekającą na zdarzenie.

+3

o.O Masz rację: Nie możesz zaimplementować 'operator =' w kategoriach 'std :: swap'; możesz go zaimplementować pod względem * własnego * ("zoptymalizowanego") 'swap'. Zwykły idiom kopiowania i wymiany dla operatorów wykorzystuje niestandardową funkcję 'swap' (funkcja składowa). – dyp

+0

Myślę, że jeśli dodaję konstruktor ruchu, to naprawi on nieskończoną rekursję (good catch btw). Przeniesienie zadania nadal będzie stanowić kopię, ale zamiana zostanie zakończona za pomocą konstruktora ruchu, prawda? –

+0

@ void.pointer 'std :: swap' robi to:' swap (A & a, A & b) {A tmp (move (a)); a = ruch (b); b = move (tmp); } 'Więc potrzebuje operatora przypisania ruchu. – dyp

8

Jeśli Foo zawiera non-statyczne członkowie danych std::vector lub std::string lub zawiera elementy danych, które zawierają vector lub string (to nawet pośrednio), to może to być bardzo skuteczny sposób spowolnić swój kod w dół. Może to być nawet bardziej skuteczne niż wywoływanie std::sleep_for, ponieważ ta ostatnia nie marnuje mocy baterii na urządzeniach mobilnych.

Powód jest taki, że przypisanie kopii, które wywołuje przypisanie kopii vector lub string, ma szansę ponownego wykorzystania pojemności pojemnika. Podczas gdy idiom zawsze wyrzuca pojemność.

Aby uzyskać więcej informacji, zobacz mój ACCU 2014 talk, slides 43-53.

W szczególności należy zwrócić uwagę na wykres wydajności, który pokazuje wzrost szybkości wywoływania operatora przypisania kopii o numerze vector, a nie na podstawie idiomu kopiowania/wymiany z elementem danych vector.

this http://howardhinnant.github.io/accu_2014_48.pdf

W najlepszym przypadku idiom copy/wymiany to tak szybko, jak przy użyciu vector „s skopiować zadanie (jeśli pojemność nie jest wystarczająca). W najgorszym przypadku (gdy pojemność jest zawsze wystarczająca), kopiowanie/zamiana jest prawie 8-krotnie wolniejsze. Średnio kopiowanie/zamiana pociąga za sobą trafienie o prędkości 70% (dla tego testu).

+0

Myślę, że sprowadza się to do tego, czy chcesz/potrzebujesz zapewnić silną gwarancję wyjątku. Ale IIRC, jesteś zwolennikiem zewnętrznej kontroli à la 'strong_assign'. – dyp

Powiązane problemy