Каждый стандартный контейнер имеет begin
и end
способ возвращения итераторов для этого контейнера. Тем не менее, C ++ 11, по- видимому , введенные свободные функции называются std::begin
и std::end
которые называют begin
и end
члены функции. Итак, вместо того, чтобы писать
auto i = v.begin();
auto e = v.end();
ты бы написал
auto i = std::begin(v);
auto e = std::end(v);
В своем выступлении « Написание современного C ++» Херб Саттер говорит, что вы всегда должны использовать бесплатные функции сейчас, когда вам нужен начальный или конечный итератор для контейнера. Тем не менее, он не вдавался в подробности относительно того, почему вы хотели бы. Глядя на код, он спасает вас всех от одного символа. Итак, что касается стандартных контейнеров, бесплатные функции кажутся совершенно бесполезными. Херб Саттер указал, что есть преимущества для нестандартных контейнеров, но, опять же, он не вдавался в подробности.
Итак, вопрос в том , что именно делать бесплатные версии функции std::begin
и std::end
сделать за вызов их соответствующие версий функций члена, и почему вы хотите использовать их?
std::
все время повторять .Ответы:
Как вы называете
.begin()
и.end()
на C-массив?Свободные функции допускают более общее программирование, потому что они могут быть добавлены впоследствии, в структуру данных, которую вы не можете изменить.
источник
end
статически объявленные массивы (int foo[5]
), используя приемы программирования шаблонов. Как только он распался на указатель, вам, конечно, не повезло.template<typename T, size_t N> T* end(T (&a)[N]) { return a + N; }
begin
иend
в массиве C до тех пор , пока вы не уже распались его к указателю сами - @Huw выговаривать это. Что касается того, почему вы хотите: представьте, что вы реорганизовали код, который использовал массив, чтобы использовать вектор (или наоборот, по любой причине). Если вы использовалиbegin
иend
, и, возможно, некоторую умную дешифровку, код реализации вообще не должен будет изменяться (за исключением, возможно, некоторых из typedefs).Рассмотрим случай, когда у вас есть библиотека, которая содержит класс:
у него есть 2 метода:
перебрать его значения, которые вы должны унаследовать от этого класса и определить
begin()
иend()
методы для случаев, когдаНо если вы всегда используете
ты можешь сделать это:
где
SpecialArrayIterator
что-то вроде:теперь
i
иe
может легально использоваться для итерации и доступа к значениям SpecialArrayисточник
template<>
линии. Вы объявляете перегрузку новой функции, не специализируя шаблон.Использование функций
begin
andend
free добавляет один уровень косвенности. Обычно это делается для большей гибкости.В этом случае я могу придумать несколько вариантов использования.
Наиболее очевидное использование для C-массивов (не c-указателей).
Другой случай - при попытке использовать стандартный алгоритм для несоответствующего контейнера (т. Е. В контейнере отсутствует
.begin()
метод). Предполагая, что вы не можете просто исправить контейнер, следующий лучший вариант - перегрузитьbegin
функцию. Херб предлагает вам всегда использовать этуbegin
функцию для обеспечения единообразия и последовательности в вашем коде. Вместо того, чтобы помнить, какие контейнеры поддерживают метод,begin
а какие нуждаются в функцииbegin
.Как и в стороне, на следующем C ++ обороты должны копировать двойки обозначения псевдо-член . Если
a.foo(b,c,d)
не определено, то вместо этого пытаетсяfoo(a,b,c,d)
. Это просто немного синтаксического сахара, чтобы помочь нам, бедным людям, которые предпочитают подчинение, а не упорядочение глаголов.источник
Чтобы ответить на ваш вопрос, бесплатные функции begin () и end () по умолчанию не делают ничего, кроме вызова функций-членов контейнера .begin () и .end (). От
<iterator>
, включается автоматически при использовании любого из стандартных контейнеров, таких как<vector>
,<list>
и т. Д., Вы получаете:Вторая часть вашего вопроса - зачем отдавать предпочтение свободным функциям, если они все равно вызывают функции-члены. Это действительно зависит от того, какой объект
v
находится в вашем примере кода. Если тип v является стандартным типом контейнера, например,vector<T> v;
не имеет значения, используете ли вы функции free или member, они делают то же самое. Если ваш объектv
более универсален, как в следующем коде:Затем использование функций-членов нарушает ваш код для массивов T = C, строк C, перечислений и т. Д. Используя функции, не являющиеся членами, вы объявляете более общий интерфейс, который люди могут легко расширить. Используя бесплатный интерфейс функции:
Код теперь работает с массивами T = C и C-строками. Теперь написание небольшого количества кода адаптера:
Мы также можем сделать ваш код совместимым с повторяемыми перечислениями. Я думаю, что главное в Хербе состоит в том, что использование свободных функций так же просто, как использование функций-членов, и это дает вашему коду обратную совместимость с типами последовательностей C и прямую совместимость с не-stl типами последовательностей (и типами future-stl!), с низкой стоимостью для других разработчиков.
источник
enum
какой-либо другой фундаментальный тип по ссылке; они будут дешевле копировать, чем косвенные.Одним из преимуществ ,
std::begin
и вstd::end
том , что они служат в качестве точек расширения для реализации стандартного интерфейса для внешних классов.Если вы хотите использовать
CustomContainer
класс с основанным на диапазоне для цикла или функции шаблона, который ожидает.begin()
и.end()
методы, вам, очевидно, придется реализовать эти методы.Если класс предоставляет эти методы, это не проблема. Если этого не произойдет, вам придется изменить его *.
Это не всегда возможно, например, при использовании внешней библиотеки, особенно коммерческой и закрытой.
В таких ситуациях
std::begin
иstd::end
пригодится, так как можно предоставить API-интерфейс итератора без изменения самого класса, а скорее с перегрузкой свободных функций.Пример: предположим, что вы хотите реализовать
count_if
функцию, которая принимает контейнер вместо пары итераторов. Такой код может выглядеть так:Теперь для любого класса, который вы хотите использовать с этим пользовательским интерфейсом
count_if
, вам нужно всего лишь добавить две бесплатные функции вместо того, чтобы изменять эти классы.Теперь в C ++ есть механизм, называемый Argument Dependent Lookup (ADL), что делает такой подход еще более гибким.
Короче говоря, ADL означает, что когда компилятор разрешает неквалифицированную функцию (т.е. функцию без пространства имен, как
begin
вместоstd::begin
), он также будет рассматривать функции, объявленные в пространствах имен своих аргументов. Например:В этом случае не имеет значения, что квалифицированные имена есть
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.
источник
Принимая во внимание, что функции, не являющиеся членами, не обеспечивают никаких преимуществ для стандартных контейнеров, их использование обеспечивает более согласованный и гибкий стиль. Если вы когда-нибудь захотите расширить существующий контейнерный класс, не относящийся к стандартному стандарту, вы предпочтете определить перегрузки свободных функций вместо изменения определения существующего класса. Так что для не-std-контейнеров они очень полезны, и всегда использование свободных функций делает ваш код более гибким, так как вы можете легко заменить std-контейнер не-std-контейнером, а базовый тип контейнера более прозрачен для вашего кода, так как поддерживает гораздо более широкий спектр реализаций контейнеров.
Но, конечно, это всегда должно быть взвешено должным образом, и чрезмерная абстракция также не годится. Хотя использование свободных функций не так уж и избыточно, тем не менее, это нарушает совместимость с кодом C ++ 03, что в юном возрасте для C ++ 11 все еще может быть проблемой для вас.
источник
boost::begin()
/end()
, так что нет никакой реальной несовместимости :)begin/end
). Так что я бы посчитал это несовместимостью с чистым C ++ 03 тоже. Но, как уже говорилось, это довольно маленькая (и становится все меньше) несовместимость, так как C ++ 11 (по крайней мере,begin/end
в частности) получает все большее и большее признание, в любом случае.В конечном итоге выгода заключается в обобщенном коде, который не зависит от контейнера. Он может работать с
std::vector
массивом или диапазоном без изменений в самом коде.Кроме того, контейнеры, даже не принадлежащие контейнерам, могут быть модифицированы таким образом, что они также могут использоваться независимо с помощью кода, использующего средства доступа, не основанные на диапазонах.
Смотрите здесь для более подробной информации.
источник