简体   繁体   中英

Argument Dependent Look (ADL) considering template arguments?

I have a couple of namespaces, each with a function template named f .

// f() and Widget
namespace A {
  struct Widget { };

  template <typename T>
  void func(const T&) { }
}

// f() and caller()
namespace B {
  template <typename T>
  void func(const T&) { }

  template <typename T>
  void caller(const T& t) {
    func(t); // error occurs here
  }
}

template <typename T>
class Wrap { };

int main() {
  Wrap<A::Widget> w{};
  B::caller(w); // triggers error
}

The above produces the following error

error: call of overloaded ‘func(const Wrap<A::Widget>&)’ is ambiguous
     func(t);
     ~~~~^~~
note: candidate: void B::func(const T&) [with T = Wrap<A::Widget>]
   void func(const T&) { }
        ^~~~
note: candidate: void A::func(const T&) [with T = Wrap<A::Widget>]
   void func(const T&) { }
        ^~~~

Why is A::func considered if Wrap is in the global namespace? Shouldn't B::caller call B::func ?

ADL doesn't just considered the arguments to the function in the case of a template. Here you have Wrap<A::Widget> as the argument to B::caller . Because caller is in namespace B , B::func is obviously considered. The reason for A::func being considered comes from the below (emphasis added)

N659 6.4.2/(2.2) [basic.lookup.argdep]

If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the innermost enclosing namespaces of its associated classes. Furthermore, if T is a class template specialization, its associated namespaces and classes also include: the namespaces and classes associated with the types of the template arguments provided for template type parameters [...]

Because A::Widget is a template argument to Wrap , A is also an associated namespace of Wrap<A::Widget>

This example can be made to compile as expected by preventing ADL by using the qualified name:

template <typename T>    
void caller(const T& t) {    
  B::func(t);      
}

or by enclosing the function name in paretheses

template <typename T>    
void caller(const T& t) {    
  (func)(t);      
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM