Search code examples
c++templatestemplate-specializationoverload-resolution

Why does using the scope resolution operator change which overloaded template in the global namespace gets called?


Consider the following code:

#include <iostream>

template <class W, class T>
void foo(W& a, T& t)
{
  std::cout << "generic" << std::endl;
}

template <template <bool> class W, class T>
void foo(W<true>& a, const T& t)
{
  foo(a, const_cast<T&>(t));
}

template <class W>
void foo(W& a, int& t)
{
  std::cout << "int" << std::endl;
}

template <bool> struct what;
template<> struct what<true> { };

int main() {
  const int ci = 10;
  what<true> wt;

  foo(wt, ci);

  return 0;
}

The output is (ideone link):

int

This makes sense to me: foo(what<true>&, const int&) matches the const_cast overload, which then calls foo(what<true>&, int&), which matches the int overload.

Yet if I change the const_cast function to the following:

template <template <bool> class W, class T>
void foo(W<true>& a, const T& t)
{
    ::foo(a, const_cast<T&>(t));
}

The output is now (ideone link):

generic

This doesn't make sense to me. Why does changing that const_cast overload of foo to call ::foo cause the generic version to get called instead of the int version?

My understanding of :: is that it was just to disambiguate which function to call in the case you have a method or a function in the global namespace. The const_cast overload still matches, which should then call ::foo(what<true>&, int&), which should match the int specialization - shouldn't it?

Further, if I change the order and place the const_cast overload using ::foo after the int specialization, then the int specialization gets called (ideone link). Why does the order of definition matter here?


Solution

  • Names declared after the template can only be found via argument-dependent name lookup. Your overload of foo for int& is only found because one of the types involved, W<true>, is a specialization of a class template declared in the global namespace. Hence ADL looks for declarations in the global namespace in the instantiation context and finds the (more specialized), desired overload.

    ::foo is a qualified-id, which suppresses ADL, hence only names declared in the definition context are considered.