std::swap()
jest używany przez wiele kontenerów standardowych (takich jak std::list
i std::vector
) podczas sortowania, a nawet przypisywania.
Jednak standardowa implementacja programu swap()
jest bardzo uogólniona i raczej nieefektywna dla typów niestandardowych.
W ten sposób wydajność można uzyskać przez przeciążenie std::swap()
implementacją specyficzną dla niestandardowego typu. Ale jak można go zaimplementować, aby był używany przez kontenery standardowe?
Odpowiedzi:
Właściwym sposobem na przeciążenie zamiany jest zapisanie go w tej samej przestrzeni nazw, w której się zamieniasz, tak aby można go było znaleźć za pomocą wyszukiwania zależnego od argumentów (ADL) . Szczególnie łatwą rzeczą jest:
źródło
std::sort
które używa ADL do zamiany elementów, jest niezgodne z C ++ 03, ale zgodne z C ++ 11. Poza tym, dlaczego -1 jest odpowiedzią opartą na fakcie, że klienci mogą używać kodu nieidiomatycznego?Uwaga Mozza 314
Oto symulacja efektów
std::algorithm
wywołania ogólnegostd::swap
i sytuacji, w której użytkownik udostępnia swap w przestrzeni nazw std. Ponieważ jest to eksperyment, w tej symulacjinamespace exp
zamiastnamespace std
.Dla mnie to drukuje:
Jeśli Twój kompilator drukuje coś innego, oznacza to, że nie implementuje poprawnie „wyszukiwania dwufazowego” dla szablonów.
Jeśli twój kompilator jest zgodny (z którymkolwiek z C ++ 98/03/11), to da to samo wyjście, które pokazuję. I w takim przypadku wydarzy się dokładnie to, czego się obawiasz. Umieszczenie twojego
swap
w przestrzeni nazwstd
(exp
) nie powstrzymało tego.Dave i ja jesteśmy członkami komitetu i zajmujemy się tym obszarem normy od dziesięciu lat (i nie zawsze w porozumieniu). Ale ta kwestia została rozstrzygnięta od dawna i oboje zgadzamy się, jak została rozwiązana. Zignoruj opinię / odpowiedź Dave'a w tej dziedzinie na własne ryzyko.
Ten problem wyszedł na jaw po opublikowaniu C ++ 98. Od około 2001 roku Dave i ja zaczęliśmy pracować w tej dziedzinie . A to jest nowoczesne rozwiązanie:
Wynik to:
Aktualizacja
Stwierdzono, że:
Pracuje! Dlaczego więc tego nie użyć?
Rozważ przypadek, że Twój
A
jest szablonem klasy:Teraz to już nie działa. :-(
Więc możesz wstawić
swap
std przestrzeni nazw i sprawić, by działało. Ale trzeba pamiętać, aby umieścićswap
wA
„s nazw dla przypadku, gdy masz szablon:A<T>
. A ponieważ w obu przypadkach będzie działać, jeśli umieścićswap
wA
„s nazw, to jest po prostu łatwiejsze do zapamiętania (a uczyć innych) po prostu zrób to, że jeden sposób.źródło
template <>
twój pierwszy przykład, otrzymuję dane wyjścioweexp::swap(A, A)
z gcc. Dlaczego więc nie preferować specjalizacji?using std::swap
jest to wyjątek od reguły „nigdy nie używaj instrukcji w plikach nagłówkowych”? W rzeczywistości, dlaczego nie włożyć dousing std::swap
środka<algorithm>
? Przypuszczam, że mogłoby to złamać kod małej mniejszości. Może zrezygnować z obsługi i ostatecznie ją włączyć?using std::swap
zakres funkcji w twoich nagłówkach. Tak,swap
to prawie słowo kluczowe. Ale nie, to nie jest słowo kluczowe. Dlatego najlepiej nie eksportować go do wszystkich przestrzeni nazw, dopóki naprawdę nie będzie to konieczne.swap
jest bardzo podobnyoperator==
. Największą różnicą jest to, że nikt nawet nie myśli o wywołaniuoperator==
z kwalifikowaną składnią przestrzeni nazw (byłoby to po prostu zbyt brzydkie).Nie możesz (zgodnie ze standardem C ++) przeciążać std :: swap, jednak możesz w szczególności dodawać specjalizacje szablonów dla własnych typów do przestrzeni nazw std. Na przykład
wtedy zastosowania w kontenerach std (i gdziekolwiek indziej) wybiorą twoją specjalizację zamiast ogólnej.
Należy również pamiętać, że zapewnienie implementacji klasy bazowej wymiany nie jest wystarczające dla typów pochodnych. Np. Jeśli masz
to zadziała dla klas bazowych, ale jeśli spróbujesz zamienić dwa obiekty pochodne, użyje wersji ogólnej ze standardowego, ponieważ zamiana szablonowa jest dokładnym dopasowaniem (i pozwala uniknąć problemu zamiany tylko części "bazowych" twoich obiektów pochodnych ).
UWAGA: zaktualizowałem to, aby usunąć niewłaściwe bity z mojej ostatniej odpowiedzi. D'oh! (dzięki puetzk i j_random_hacker za wskazanie tego)
źródło
Chociaż prawdą jest, że generalnie nie należy dodawać rzeczy do przestrzeni nazw std ::, dodawanie specjalizacji szablonów dla typów zdefiniowanych przez użytkownika jest szczególnie dozwolone. Przeciążanie funkcji nie jest. To subtelna różnica :-)
Specjalizacja std :: swap wyglądałaby następująco:
Bez bitu template <> byłoby to przeciążenie, które jest nieokreślone, a nie specjalizacja, która jest dozwolona. @ Wilka sugerowane podejście do zmiany domyślnej przestrzeni nazw może działać z kodem użytkownika (z powodu wyszukiwania Koeniga preferującego wersję bez przestrzeni nazw), ale nie jest to gwarantowane, a tak naprawdę nie powinno (implementacja STL powinna używać w pełni -qualified std :: swap).
Istnieje wątek na comp.lang.c ++. Moderowany z długą dyskusją na ten temat. Większość z nich dotyczy jednak częściowej specjalizacji (na którą obecnie nie ma dobrego sposobu).
źródło
vector
wersji i będą wykorzystywane .::swap
Przeciążenie została dodana jest bardziej wyspecjalizowane niżstd::swap
przeciążeniemvector
, zapisuje więc połączenie i bez specjalizacji to ostatnie jest istotne. Nie jestem pewien, jak to jest praktyczny problem (ale nie twierdzę też, że to dobry pomysł!).