Установленная идиома для вызова swap
является:
using std::swap
swap(foo, bar);
Сюда, swap
может быть перегружен для пользовательских типов за пределами std
Пространство имен.
Должны ли мы ссылаться begin
а также end
таким же образом?
using std::begin;
using std::end;
some_algorithm(begin(some_container), end(some_container));
Или мы должны просто написать:
some_algorithm(std::begin(some_container), std::end(some_container));
Используя using
—декларация как это правильный путь ИМО. Это также то, что стандарт делает с диапазоном для цикла: если нет begin
или же end
члены присутствуют, то он будет называть begin(x)
а также end(x)
с std
в качестве связанного пространства имен (т.е. он найдет std::begin
а также std::end
если ADL не найдет не-участника begin
а также end
).
Если вы найдете это письмо using std::begin; using std::end;
все время утомительно, то вы можете использовать adl_begin
а также adl_end
функции ниже:
namespace aux {
using std::begin;
using std::end;
template<class T>
auto adl_begin(T&& x) -> decltype(begin(std::forward<T>(x)));
template<class T>
auto adl_end(T&& x) -> decltype(end(std::forward<T>(x)));
template<class T>
constexpr bool is_array()
{
using type = typename std::remove_reference<T>::type;
return std::is_array<type>::value;
}
} // namespace aux
template<class T,
class = typename std::enable_if<!aux::is_array<T>()>::type>
auto adl_begin(T&& x) -> decltype(aux::adl_begin(std::forward<T>(x)))
{
using std::begin;
return begin(std::forward<T>(x));
}
template<class T,
class = typename std::enable_if<!aux::is_array<T>()>::type>
auto adl_end(T&& x) -> decltype(aux::adl_end(std::forward<T>(x)))
{
using std::end;
return end(std::forward<T>(x));
}
template<typename T, std::size_t N>
T* adl_begin(T (&x)[N])
{
return std::begin(x);
}
template<typename T, std::size_t N>
T* adl_end(T (&x)[N])
{
return std::end(x);
}
Этот код довольно чудовищный. Надеюсь, с C ++ 14 это может стать менее загадочным:
template<typename T>
concept bool Not_array()
{
using type = std::remove_reference_t<T>;
return !std::is_array<type>::value;
}
decltype(auto) adl_begin(Not_array&& x)
{
using std::begin;
return begin(std::forward<Not_array>(x));
}
decltype(auto) adl_end(Not_array&& x)
{
using std::end;
return end(std::forward<Not_array>(x));
}
template<typename T, std::size_t N>
T* adl_begin(T (&x)[N])
{
return std::begin(x);
}
template<typename T, std::size_t N>
T* adl_end(T (&x)[N])
{
return std::end(x);
}
Отказ от ответственности: Для педантичных типов (или педантов, если вы хотите быть педантичными …), я обычно ссылаюсь на слово «Перегрузка» здесь как «Создайте функции с именами begin
а также end
и делать using std::begin; using std::end;
«., который, поверьте мне, совсем не утомителен для меня, но очень труден для чтения и избыточен для чтения. :p.
Я в основном дам вам возможные варианты использования такой техники, а позже сделаю вывод.
begin
а также end
методы не действуют как стандартные контейнерыОдна ситуация, когда вам может понадобиться перегрузить std::begin
а также std::end
функции, когда вы используете begin
а также end
методы вашего типа иным способом, кроме предоставления итераторного доступа к элементам объекта, и они хотят иметь перегрузки std::begin
а также std::end
вызовите методы начала и конца, используемые для итерации.
struct weird_container {
void begin() { std::cout << "Start annoying user." }
void end() { std::cout << "Stop annoying user." }
iterator iter_begin() { /* return begin iterator */ }
iterator iter_end() { /* return end iterator */ }
};auto begin(weird_container& c) {
return c.iter_begin();
}
auto end(weird_container& c) {
return c.iter_end();
}
Тем не менее, вы не будете и не должны делать такие сумасшедшие вещи, как Range-For сломается, если используется с объектом weird_container
согласно правилам ранжирования, weird_container::begin()
а также weird_container::end()
методы будут найдены до автономных вариантов функций.
Таким образом, этот случай приводит аргумент не использовать то, что вы предложили, так как это нарушит одну очень полезную функцию языка.
begin
а также end
методы не определены вообщеДругой случай, когда вы не определяете begin
а также end
методы. Это более распространенный и применимый случай, когда вы хотите расширить свой тип для возможности повторения без изменения интерфейса класса.
struct good_ol_type {
...
some_container& get_data();
...
};
auto begin(good_ol_type& x) {
return x.get_data().begin();
}
auto end(good_ol_type& x) {
return x.get_data().end();
}
Это позволит вам использовать некоторые изящные функции на good_ol_type
(алгоритмы, range-for и т. д.) без изменения его интерфейса! Это соответствует рекомендации Херба Саттера о расширении функциональности типов с помощью функций, не являющихся членами.
Это хороший случай, когда вы действительно хотите перегрузить std:;begin
а также std::end
,
Как у меня нет Когда-либо видел, как кто-то делает что-то подобное в первом случае (кроме моего примера), тогда вы действительно захотите использовать то, что предложили, и перегрузить std::begin
а также std::end
где применимо.
Я не включил здесь случай, когда вы определили оба begin
а также end
методы и begin
а также end
функции, которые делают разные вещи, чем методы. Я полагаю, что такая ситуация придумана, неправильно сформирована и / или сделана программистом, который не имел большого опыта в изучении отладчика или чтении ошибок шаблона.
Если ваш some_container является стандартным контейнером, префикс std :: не нужен
#include <iostream>
#include <vector>
#include <algorithm>
int main(){
std::vector<int>v { 1, 7, 1, 3, 6, 7 };
std::sort( begin(v), end(v) ); // here ADL search finds std::begin, std::end
}
документация из swap
указывает, что идиома, на которую вы ссылаетесь, является обычной практикой в библиотеке stl
Многие компоненты стандартной библиотеки (в пределах std) вызывают обмен в
неподходящий способ разрешить пользовательские перегрузки для неосновных типов
вызываться вместо этой универсальной версии: пользовательские перегрузки свопа
объявлены в том же пространстве имен, что и тип, для которого они предоставляются
быть выбранным через аргумент-зависимый поиск по этому универсальному
версия.
Нет такой вещи в документации для begin
а также end
,
По этой причине вы можете использовать
using std::begin;
using std::end;
some_algorithm(begin(some_container), end(some_container));
соглашение о вызове, но вы должны знать, что это соглашение, которое не распространяется, например, на стандартные алгоритмы, но только для вашего кода.