Ambiguous function call due to ADL

I have been bitten by this problem a couple of times, and so are my colleagues. At compilation

#include <deque> #include <boost/algorithm/string/find.hpp> #include <boost/operators.hpp> template< class Rng, class T > typename boost::range_iterator<Rng>::type find( Rng& rng, T const& t ) { return std::find( boost::begin(rng), boost::end(rng), t ); } struct STest { bool operator==(STest const& test) const { return true; } }; struct STest2 : boost::equality_comparable<STest2> { bool operator==(STest2 const& test) const { return true; } }; void main() { std::deque<STest> deq; find( deq, STest() ); // works find( deq, STest2() ); // C2668: 'find' : ambiguous call to overloaded function } 

... the VS9 compiler does not work when compiling the second search. This is because STest2 inherits from the type defined in the boost namespace, which starts the compiler to check for ADL, which finds boost::algorithm::find(RangeT& Input, const FinderT& Finder) .

The obvious solution is to prefix the call to find(โ€ฆ) with " :: ", but why is this necessary? There is a perfectly acceptable match in the global namespace, so why refer to a search that depends on the arguments? Can anyone explain this rationale here?

+7
c ++ visual-c ++ templates argument-dependent-lookup
source share
4 answers

ADL is not a fault tolerant mechanism for use with "normal" overload resolution; the functions found by ADLs are just as viable as the functions found by regular searches.

If ADL was a backup solution, then you could easily fall into the trap, because the function was used even when there was another function that was better, but was visible only through ADL. This seems especially strange in the case of (for example) operator overloads. You would not want two objects to be compared using operator== for types that they could be implicitly converted when operator== is excellent in the corresponding namespace.

+7
source share

I will add the obvious answer myself, because I just did some research on this issue:

C ++ 03 3.4.2

ยง2 For each type of argument T, in a function call there is a set of zero or more related namespaces [...] Sets of namespaces and classes are defined as follows:

[...]

- If T is a class type (including associations), its associated classes: the class itself; the class of which he is a member, if any; and its direct and indirect base classes . Its associated namespaces are namespaces in which related classes are defined.

ยง 2a If a regular unqualified name search finds an declaration of a member function of a class , the associated namespaces and classes are not considered. Otherwise, the set of declarations found during the search for a function name is the union of the set of declarations found using the regular unqualified search and the set of declarations found in namespaces and classes associated with argument types.

At least this is a standard coincidence, but I still don't understand the reason.

+3
source share

Consider a mystream that inherits from std::ostream . You would like your type to support all the << operators that are defined for std::ostream usually in the std namespace. Thus, base classes are associated classes for ADL.

I think this also follows from the substitution principle - and functions in the class namespace are considered part of its interface (see Herb Sutter "What's in the class?"). Thus, an interface that works in a base class must continue to work on the derived class.

You can also get around this by disabling ADL:

 (find)( deq, STest2() ); 
+3
source share

I think you yourself posed the problem:

in the global namespace

Functions in the global namespace are considered last. This is the most external coverage by definition. Any function with the same name (not necessarily applicable) that is in a closer space (from the point of view of the call) will be raised first.

 template <typename Rng, typename T> typename Rng::iterator find( Rng& rng, T const& t ); namespace foo { bool find(std::vector<int> const& v, int); void method() { std::deque<std::string> deque; auto it = find(deque, "bar"); } } 

Here (if vector or deque does not include algorithm , which is allowed), the only method that will be selected during the name lookup will be:

 bool foo::find(std::vector<int> const&, int); 

If algorithm is somehow enabled, it will also:

 template <typename FwdIt> FwdIt std::find(FwdIt begin, FwdIt end, typename std::iterator_traits<FwdIt>::value_type const& value); 

And, of course, overload resolution will not claim that there is no match.

Note that searching by name is very dumb: neither argument nor argument type is considered!

Therefore, there are only two kinds of free functions that you should use in C ++:

  • Those that are part of the interface of a class declared in the same namespace are caught by ADL
  • Those that are not, and that you must explicitly qualify to avoid this type of problem

If you fall out of these rules, it may work or not, depending on what is included and what is very inconvenient.

+1
source share

All Articles