Вызывать начало и конец с помощью директивы using?

Установленная идиома для вызова 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));

16

Решение

Используя 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);
}
9

Другие решения

Отказ от ответственности: Для педантичных типов (или педантов, если вы хотите быть педантичными …), я обычно ссылаюсь на слово «Перегрузка» здесь как «Создайте функции с именами begin а также end и делать using std::begin; using std::end;«., который, поверьте мне, совсем не утомителен для меня, но очень труден для чтения и избыточен для чтения. :p.


Я в основном дам вам возможные варианты использования такой техники, а позже сделаю вывод.

Случай 1 — Ваш 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() методы будут найдены до автономных вариантов функций.

Таким образом, этот случай приводит аргумент не использовать то, что вы предложили, так как это нарушит одну очень полезную функцию языка.

Случай 2 — 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 функции, которые делают разные вещи, чем методы. Я полагаю, что такая ситуация придумана, неправильно сформирована и / или сделана программистом, который не имел большого опыта в изучении отладчика или чтении ошибок шаблона.

6

Если ваш 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
}
1

документация из swap указывает, что идиома, на которую вы ссылаетесь, является обычной практикой в ​​библиотеке stl

Многие компоненты стандартной библиотеки (в пределах std) вызывают обмен в
неподходящий способ разрешить пользовательские перегрузки для неосновных типов
вызываться вместо этой универсальной версии: пользовательские перегрузки свопа
объявлены в том же пространстве имен, что и тип, для которого они предоставляются
быть выбранным через аргумент-зависимый поиск по этому универсальному
версия.

Нет такой вещи в документации для begin а также end,

По этой причине вы можете использовать

using std::begin;
using std::end;
some_algorithm(begin(some_container), end(some_container));

соглашение о вызове, но вы должны знать, что это соглашение, которое не распространяется, например, на стандартные алгоритмы, но только для вашего кода.

0
По вопросам рекламы [email protected]