ADL and container functions (begin, end, etc)
C++11 and later define free functions begin
, end
, empty
, etc in namespace std. For most containers these functions invoke the corresponding member function. But for some containers (like valarray
) these free functions are overloaded (initializer_list does not have a member begin()). So to iterate over any container free functions should be used and to find functions for container from namespaces other than std
ADL should be used:
template<typename C>
void foo(C c)
{
using std::begin;
using std::end;
using std::empty;
if (empty(c)) throw empty_container();
for (auto i = begin(c); i != end(c); ++i) { /* do something */ }
}
Question 1 : Am I correct? Are begin
and end
expected to be found via ADL?
But ADL rules specify that if type of an argument is a class template specialization ADL includes namespaces of all template arguments. And then Boost.Range library comes into play, it defines boost::begin
, boost::end
, etc. These functions are defined like this:
template< class T >
inline BOOST_DEDUCED_TYPENAME range_iterator<T>::type begin( T& r )
{
return range_begin( r );
}
If I use std::vector<boost::any>
and a Boost.Range I run into trouble. std::begin and boost::begin overloads are ambiguous. That it, I can not write template code that will find a free begin
via ADL. If I explicitly use std::begin
I expect that any non- std::
container has a member begin
.
Question 2 : What shall I do in this case?
Rely on the presence of member function? Simplest way.
Ban Boost.Range? Well, algorithms that take container instead of a pair of iterators are convinient. Boost.Range adaptors (containers that lazily apply an algorithm to a container) are also convinient. But if I do not use Boost.Range in my code it still can be used in a boost library (other than Range). This make template code really fragile.
Ban Boost?
A few years ago, I had a similar problem where my code suddenly started getting ambiguities between std::begin
and boost::begin
. I found it was due to using Boost.Operator to aid in defining a class, even though it was not even a public base class or apparent to the user of the types involved. A random change somewhere caused #include <boost/range/begin.hpp>
to be present in the nested include files somewhere, thus making boost::begin
visible to the compiler.
I complained to the mailing list about the putting of classes directly in the Boost namespace, rather than in a nested class and exposing them via using
declarations; all stuff defined directly in the Boost namespace could potentially step on each other via accidental ADL.
I just tried to reproduce this today, and it seems quite resilient against such ambiguities now! Looking at the definition, boost::begin
is itself in an inner namespace so it can never be found via unqualified lookup if you had not supplied your own using boost::begin;
in your own scope.
I don't know how long ago this fix took place. (If you can still reproduce it, please post a complete program with version and platform details.)
So your answer is:
For Boost, don't worry about it anymore (upgrade Boost if necessary).
For new code, never define a free function named begin
in the same namespace as any of its defined types.
上一篇: 在.NET中了解ConfigurationManager
下一篇: ADL和容器功能(开始,结束等)