Każdy standardowy kontener ma metodę begin
i end
do zwracania iteratorów dla tego kontenera. Jednak C ++ 11 najwyraźniej wprowadził wolne funkcje o nazwie std::begin
i std::end
które wywołują funkcje begin
i end
. Zamiast pisać
auto i = v.begin();
auto e = v.end();
piszesz
auto i = std::begin(v);
auto e = std::end(v);
W swoim wystąpieniu, Writing Modern C ++ , Herb Sutter mówi, że powinieneś zawsze korzystać z darmowych funkcji teraz, gdy chcesz rozpocząć lub zakończyć iterator dla kontenera. Nie wyjaśnia on jednak szczegółowo, dlaczego chcesz. Patrząc na kod, oszczędza ci to jednego znaku. Jeśli chodzi o standardowe kontenery, darmowe funkcje wydają się całkowicie bezużyteczne. Herb Sutter wskazał, że istnieją niestandardowe pojemniki, ale ponownie nie wdał się w szczegóły.
Pytanie brzmi: co dokładnie robią wolne wersje funkcji std::begin
i std::end
poza wywoływaniem ich odpowiednich wersji funkcji członkowskich i dlaczego chcesz ich używać?
std::
cały czas powtarzać .Odpowiedzi:
Jak dzwonisz
.begin()
i.end()
macierz C?Bezpłatne funkcje pozwalają na bardziej ogólne programowanie, ponieważ można je później dodać, w strukturze danych, której nie można zmienić.
źródło
end
tablice dla deklarowanych statycznie (int foo[5]
) przy użyciu sztuczek programistycznych. Gdy już rozpadnie się na wskaźnik, oczywiście nie masz szczęścia.template<typename T, size_t N> T* end(T (&a)[N]) { return a + N; }
begin
iend
na tablicy C, o ile nie zostało to zepsute go do wskaźnika siebie - @Huw zapis przyjmuje postać go. Co do tego, dlaczego chcesz: wyobraź sobie, że dokonałeś refaktoryzacji kodu, który używał tablicy, aby użyć wektora (lub odwrotnie, z jakiegokolwiek powodu). Jeśli używaszbegin
iend
być może sprytnego typowania, kod implementacyjny nie będzie musiał się w ogóle zmieniać (z wyjątkiem niektórych typów typedef).Rozważ przypadek, gdy masz bibliotekę zawierającą klasę:
ma 2 metody:
aby iterować po jego wartościach, musisz odziedziczyć po tej klasie i zdefiniować
begin()
orazend()
metody dla przypadków kiedyAle jeśli zawsze używasz
możesz to zrobić:
gdzie
SpecialArrayIterator
jest coś takiego:teraz
i
ie
mogą być legalnie używane do iteracji i uzyskiwania dostępu do wartości SpecialArrayźródło
template<>
wierszy. Zadeklarujesz przeciążenie nowej funkcji, nie specjalizując się w szablonie.Używanie
begin
iend
bezpłatne funkcje dodaje jedną warstwę zadnie. Zwykle odbywa się to w celu zapewnienia większej elastyczności.W tym przypadku mogę wymyślić kilka zastosowań.
Najbardziej oczywistym zastosowaniem są tablice C (nie wskaźniki C).
Innym jest próba użycia standardowego algorytmu na niezgodnym kontenerze (tzn. W kontenerze brakuje
.begin()
metody). Zakładając, że nie możesz po prostu naprawić kontenera, następną najlepszą opcją jest przeciążeniebegin
funkcji. Herb sugeruje, abyś zawsze używał tejbegin
funkcji do promowania jednolitości i spójności kodu. Zamiast pamiętać, które kontenery obsługują metodębegin
i które wymagają funkcjibegin
.Jak z boku, w kolejnym C ++ obr należy skopiować D's zapisu pseudo użytkownika . Jeśli
a.foo(b,c,d)
nie jest zdefiniowane, zamiast tego próbujefoo(a,b,c,d)
. To tylko niewielki cukier składniowy, który pomaga nam biednym ludziom, którzy wolą sortowanie według podmiotu niż czasownika.źródło
Aby odpowiedzieć na twoje pytanie, darmowe funkcje start () i end () domyślnie wykonują jedynie wywołanie funkcji członka .begin () i .end () kontenera. Od
<iterator>
, włączone automatycznie podczas korzystania dowolny ze standardowych pojemnikach, takich jak<vector>
,<list>
itd, można uzyskać:Drugą częścią twojego pytania jest to, dlaczego wolisz funkcje bezpłatne, jeśli i tak wywołują funkcje członka. To naprawdę zależy od tego, jaki obiekt
v
znajduje się w twoim przykładowym kodzie. Jeśli typ v jest standardowym typem kontenera, tak jakvector<T> v;
wtedy, nie ma znaczenia, czy korzystasz z funkcji darmowej czy składowej, robią to samo. Jeśli Twój obiektv
jest bardziej ogólny, jak w poniższym kodzie:Następnie użycie funkcji składowych powoduje uszkodzenie kodu dla tablic T = C, ciągów C, wyliczeń itp. Korzystając z funkcji nie będących członkami, reklamujesz bardziej ogólny interfejs, który ludzie mogą łatwo rozszerzyć. Korzystając z darmowego interfejsu funkcji:
Kod działa teraz z tablicami T = C i ciągami C. Teraz piszę niewielką ilość kodu adaptera:
Możemy sprawić, że Twój kod będzie również zgodny z iterowalnymi wyliczeniami. Myślę, że głównym punktem Herb jest to, że korzystanie z darmowych funkcji jest tak samo łatwe, jak korzystanie z funkcji składowych, i zapewnia twojemu kodowi wsteczną kompatybilność z typami sekwencji C i kompatybilność z innymi typami sekwencji nie stl (i przyszłymi typami stl!), przy niskich kosztach dla innych programistów.
źródło
enum
żadnego innego podstawowego typu przez odniesienie; kopiowanie będzie tańsze niż pośrednie.Jedną z zalet
std::begin
istd::end
jest to, że służą one jako punkty rozszerzeń dla realizacji standardowy interfejs dla klas zewnętrznych.Jeśli chcesz użyć
CustomContainer
klasy z funkcją pętli lub funkcji szablonu opartą na zakresie, która oczekuje.begin()
i.end()
metodami, oczywiście musisz zaimplementować te metody.Jeśli klasa udostępnia te metody, nie stanowi to problemu. Jeśli nie, musisz go zmodyfikować *.
Nie zawsze jest to możliwe, na przykład w przypadku korzystania z biblioteki zewnętrznej, szczególnie komercyjnej i zamkniętej.
W takich sytuacjach
std::begin
istd::end
przydają się, ponieważ można zapewnić interfejs API iteratora bez modyfikowania samej klasy, ale raczej przeciążając wolne funkcje.Przykład: załóżmy, że chcesz zaimplementować
count_if
funkcję, która pobiera kontener zamiast pary iteratorów. Taki kod może wyglądać następująco:Teraz dla każdej klasy, której chcesz używać z tym niestandardem
count_if
, musisz tylko dodać dwie bezpłatne funkcje, zamiast modyfikować te klasy.Teraz C ++ ma mechanisim o nazwie Argument Dependent Lookup (ADL), co czyni takie podejście jeszcze bardziej elastycznym.
W skrócie, ADL oznacza, że gdy kompilator rozpozna funkcję niekwalifikowaną (tj. Funkcję bez przestrzeni nazw, jak
begin
zamiast zamiaststd::begin
), to również rozważy funkcje zadeklarowane w przestrzeni nazw swoich argumentów. Na przykład:W tym przypadku nie ma znaczenia, że są to kwalifikowane nazwy
some_lib::begin
isome_lib::end
- ponieważCustomContainer
jestsome_lib::
również w, kompilator użyje tych przeciążeń wcount_if
.To również powód, dla posiadania
using std::begin;
iusing std::end;
wcount_if
. To pozwala nam korzystać z niewykwalifikowanych,begin
aend
tym samym pozwalając na ADL i pozwalając kompilatorowi wybieraćstd::begin
istd::end
kiedy nie ma innych alternatyw.Możemy zjeść ciasteczko i mieć ciasteczko - tj. Mieć sposób na zapewnienie niestandardowej implementacji
begin
/end
podczas gdy kompilator może wrócić do standardowych.Niektóre uwagi:
Z tego samego powodu, istnieją inne podobne funkcje:
std::rbegin
/rend
,std::size
astd::data
.Jak wspominają inne odpowiedzi,
std::
wersje mają przeciążenia dla nagich tablic. Jest to przydatne, ale jest po prostu szczególnym przypadkiem tego, co opisałem powyżej.Korzystanie
std::begin
ze znajomych i przyjaciół jest szczególnie dobrym pomysłem podczas pisania kodu szablonu, ponieważ dzięki temu szablony są bardziej ogólne. W przypadku innych niż szablon równie dobrze możesz użyć metod, jeśli mają zastosowanie.PS Wiem, że ten post ma prawie 7 lat. Natknąłem się na to, ponieważ chciałem odpowiedzieć na pytanie oznaczone jako duplikat i odkryłem, że w żadnej odpowiedzi nie ma mowy o ADL.
źródło
Podczas gdy funkcje nie będące członkami nie zapewniają żadnych korzyści dla standardowych kontenerów, użycie ich wymusza bardziej spójny i elastyczny styl. Jeśli chcesz w pewnym momencie rozszerzyć istniejącą niestabilną klasę kontenera, wolisz zdefiniować przeciążenia wolnych funkcji, zamiast zmieniać definicję istniejącej klasy. Dlatego w przypadku kontenerów innych niż standardowe są one bardzo przydatne i zawsze dzięki bezpłatnym funkcjom kod staje się bardziej elastyczny, ponieważ można łatwiej zastąpić standardowy kontener innym niż standardowy, a podstawowy typ kontenera jest bardziej przejrzysty dla kodu, ponieważ obsługuje znacznie szerszą gamę implementacji kontenerów.
Ale oczywiście zawsze musi to być odpowiednio ważone, a nadmierna abstrakcja też nie jest dobra. Chociaż korzystanie z darmowych funkcji nie jest nadmierną abstrakcją, to jednak łamie kompatybilność z kodem C ++ 03, co w młodym wieku C ++ 11 może nadal stanowić dla ciebie problem.
źródło
boost::begin()
/end()
, więc nie ma prawdziwej niezgodności :)begin/end
). Uważałbym więc, że jest to również niezgodność z czystym C ++ 03. Ale, jak powiedziano, jest to raczej niewielka (i coraz mniejsza) niezgodność, ponieważ C ++ 11 (przynajmniejbegin/end
w szczególności) jest coraz bardziej adoptowany.Ostatecznie korzyść tkwi w kodzie uogólnionym tak, że jest on niezależny od kontenera. Może działać na
std::vector
tablicy, tablicy lub zakresie bez zmian w samym kodzie.Dodatkowo kontenery, nawet kontenery nie będące własnością, mogą być modernizowane w taki sposób, że mogą być również używane agnostycznie za pomocą kodu przy użyciu akcesoriów nie opartych na zakresie członów.
Zobacz tutaj po więcej szczegółów.
źródło