Я нашел этот интересный бит в Boost.Range:
При предоставлении отдельно стоящие функции range_begin/end()
, документы утверждают, что:
…
range_begin()
а такжеrange_end()
должен быть перегружен для обоихconst
а такжеmutable
ссылочные аргументы.
И действительно, глядя на их значения по умолчанию в end.hpp
, мы видим:
//////////////////////////////////////////////////////////////////////
// pair
//////////////////////////////////////////////////////////////////////
template< typename Iterator >
inline Iterator range_end( const std::pair<Iterator,Iterator>& p )
{
return p.second;
}
template< typename Iterator >
inline Iterator range_end( std::pair<Iterator,Iterator>& p )
{
return p.second;
}
Вы заметите (и пример приведен в документах также делает это), что обе версии возвращают одинаковые Iterator
тип.
Зачем нам обе эти перегрузки в первую очередь? Это сделать ADL Работа?
Вам, очевидно, нужно const &
версия, потому что в противном случае ваш range_begin
было бы невозможно вызвать для объектов с постоянным соответствием.
Менее очевидно, почему вы также нуждаетесь в &
версия, но она проста: если вы ее не предоставите, ваша пользовательская функция будет хуже, чем собственная версия Boost.
Вот краткий пример без Boost:
namespace M {
struct S { };
void f(const S &);
}
namespace N {
template <typename T>
void f(T &);
template <typename T>
void g(T &t) { f(t); }
}
void h() {
M::S s {};
N::g(s);
}
Здесь, во время создания N::g<M::S>
безоговорочный вызов f(t)
сделан, и аргумент t
имеет тип M::S
, Есть два кандидата: N::f<M::S>
находится в том же пространстве имен, но ADL также находит M::f
, Первый параметр M::S &
, Последний является const M::S &
, Это означает, что первое лучше подходит, даже если вы действительно хотите версию в пространстве имен M
использоваться.
Дополнительная перегрузка M::f(S &)
избегает этой проблемы.
Других решений пока нет …