Considering:
#include <cassert>
#include <boost/range/irange.hpp>
#include <boost/range/algorithm.hpp>
int main() {
auto range = boost::irange(1, 4);
assert(boost::find(range, 4) == end(range));
}
this gives:
main.cpp:8:37: error: use of undeclared identifier 'end'
Considering that if you write using boost::end;
it works just fine, which implies that boost::end
is visible:
Why is ADL not working and finding boost::end
in the expression end(range)
? And if it's intentional, what's the rationale behind it?
To be clear, the expected result would be similar to what happens in this example using std::find_if
and unqualified end(vec)
.
In boost/range/end.hpp
they explicitly block ADL by putting end
in a range_adl_barrier
namespace, then using namespace range_adl_barrier;
to bring it into the boost
namespace.
As end
is not actually from ::boost
, but rather from ::boost::range_adl_barrier
, it is not found by ADL.
Their reasoning is described in boost/range/begin.hpp
:
// Use a ADL namespace barrier to avoid ambiguity with other unqualified
// calls. This is particularly important with C++0x encouraging
// unqualified calls to begin/end.
no examples are given of where this causes a problem, so I can only theorize what they are talking about.
Here is an example I have invented of how ADL can cause ambiguity:
namespace foo {
template<class T>
void begin(T const&) {}
}
namespace bar {
template<class T>
void begin(T const&) {}
struct bar_type {};
}
int main() {
using foo::begin;
begin( bar::bar_type{} );
}
live example. Both foo::begin
and bar::begin
are equally valid functions to call for the begin( bar::bar_type{} )
in that context.
This could be what they are talking about. Their boost::begin
and std::begin
might be equally valid in a context where you have using std::begin
on a type from boost
. By putting it in a sub-namespace of boost
, std::begin
gets called (and works on ranges, naturally).
If the begin
in the namespace boost
had been less generic, it would be preferred, but that isn't how they wrote it.