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.

链接地址: http://www.djcxy.com/p/72940.html

上一篇: 在.NET中了解ConfigurationManager

下一篇: ADL和容器功能(开始,结束等)