Одним из преимуществ , std::begin
и в std::end
том , что они служат в качестве точек расширения для реализации стандартного интерфейса для внешних классов.
Если вы хотите использовать CustomContainer
класс с основанным на диапазоне для цикла или функции шаблона, который ожидает .begin()
и .end()
методы, вам, очевидно, придется реализовать эти методы.
Если класс предоставляет эти методы, это не проблема. Если этого не произойдет, вам придется изменить его *.
Это не всегда возможно, например, при использовании внешней библиотеки, особенно коммерческой и закрытой.
В таких ситуациях std::begin
и std::end
пригодится, так как можно предоставить API-интерфейс итератора без изменения самого класса, а скорее с перегрузкой свободных функций.
Пример: предположим, что вы хотите реализовать count_if
функцию, которая принимает контейнер вместо пары итераторов. Такой код может выглядеть так:
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));
}
Теперь для любого класса, который вы хотите использовать с этим пользовательским интерфейсом count_if
, вам нужно всего лишь добавить две бесплатные функции вместо того, чтобы изменять эти классы.
Теперь в C ++ есть механизм, называемый Argument Dependent Lookup
(ADL), что делает такой подход еще более гибким.
Короче говоря, ADL означает, что когда компилятор разрешает неквалифицированную функцию (т.е. функцию без пространства имен, как begin
вместо std::begin
), он также будет рассматривать функции, объявленные в пространствах имен своих аргументов. Например:
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);
В этом случае не имеет значения, что квалифицированные имена есть some_lib::begin
и some_lib::end
- поскольку CustomContainer
он some_lib::
тоже, компилятор будет использовать эти перегрузки в count_if
.
Это также причина того, чтобы using std::begin;
и using std::end;
в count_if
. Это позволяет нам использовать неквалифицированную begin
и end
, следовательно, разрешающую ADL и
позволяющую компилятору выбирать, std::begin
и std::end
когда никакие другие альтернативы не найдены.
Мы можем использовать cookie-файл и иметь cookie-файл, то есть иметь способ обеспечить пользовательскую реализацию begin
/, в end
то время как компилятор может вернуться к стандартным.
Некоторые заметки:
По той же причине есть и другие похожие функции: std::rbegin
/ rend
,
std::size
и std::data
.
Как уже упоминалось в других ответах, std::
версии имеют перегрузки для голых массивов. Это полезно, но это просто частный случай того, что я описал выше.
Использование std::begin
and friends особенно полезно при написании кода шаблона, потому что это делает эти шаблоны более общими. Для не шаблонов вы можете также использовать методы, когда это применимо.
PS Я в курсе, что этому посту почти 7 лет. Я столкнулся с этим, потому что я хотел ответить на вопрос, который был отмечен как дубликат, и обнаружил, что ни один ответ здесь не упоминает ADL.