Jedną z zalet std::begini std::endjest to, że służą one jako punkty rozszerzeń dla realizacji standardowy interfejs dla klas zewnętrznych.
Jeśli chcesz użyć CustomContainerklasy 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::begini std::endprzydają 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_iffunkcję, która pobiera kontener zamiast pary iteratorów. Taki kod może wyglądać następująco:
template<typename ContainerType, typename PredicateType>
std::size_t count_if(const ContainerType& container, PredicateType&& predicate)
{
using std::begin;
using std::end;
return std::count_if(begin(container), end(container),
std::forward<PredicateType&&>(predicate));
}
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 beginzamiast zamiast std::begin), to również rozważy funkcje zadeklarowane w przestrzeni nazw swoich argumentów. Na przykład:
namesapce some_lib
{
// let's assume that CustomContainer stores elements sequentially,
// and has data() and size() methods, but not begin() and end() methods:
class CustomContainer
{
...
};
}
namespace some_lib
{
const Element* begin(const CustomContainer& c)
{
return c.data();
}
const Element* end(const CustomContainer& c)
{
return c.data() + c.size();
}
}
// somewhere else:
CustomContainer c;
std::size_t n = count_if(c, somePredicate);
W tym przypadku nie ma znaczenia, że są to kwalifikowane nazwy some_lib::begini some_lib::end
- ponieważ CustomContainerjest some_lib::również w, kompilator użyje tych przeciążeń w count_if.
To również powód, dla posiadania using std::begin;i using std::end;w count_if. To pozwala nam korzystać z niewykwalifikowanych, begina endtym samym pozwalając na ADL i
pozwalając kompilatorowi wybierać std::begini std::endkiedy nie ma innych alternatyw.
Możemy zjeść ciasteczko i mieć ciasteczko - tj. Mieć sposób na zapewnienie niestandardowej implementacji begin/ endpodczas gdy kompilator może wrócić do standardowych.
Niektóre uwagi:
Z tego samego powodu, istnieją inne podobne funkcje: std::rbegin/ rend,
std::sizea std::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::beginze 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.