Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overload Resolution in a Namespace

I am attempting to call an overloaded function inside a namespace and am struggling a bit.

Working Example 1: No namespace

class C {};

inline void overloaded(int) {}

template<typename T> void try_it(T value) {
  overloaded(value);
}

inline void overloaded(C) {}


int main()
{
  try_it(1);
  C c;
  try_it(c);
  return 0;
}

Working Example 2: All overloads defined before template

class C {};

namespace n {
  inline void overloaded(int) {}
  inline void overloaded(C) {}
}

template<typename T> void try_it(T value) {
  n::overloaded(value);
}

int main()
{
  try_it(1);
  C c;
  try_it(c);
  return 0;
}

Broken Example 3: Some overloads after the template

class C {};

namespace n {
  inline void overloaded(int) {}
}

template<typename T> void try_it(T value) {
  n::overloaded(value);
}

namespace n {
  inline void overloaded(C) {}
}

int main()
{
  try_it(1);
  C c;
  try_it(c); // /tmp/test.cpp: In function ‘void try_it(T) [with T = C]’:
             // /tmp/test.cpp:19:15:   instantiated from here
             // /tmp/test.cpp:8:7: error: cannot convert ‘C’ to ‘int’ for argument ‘1’ to ‘void n::overloaded(int)’

  return 0;
}

Why is this the case? What do I need to do to be able to declare or define an overload after the template function?

like image 231
Dark Falcon Avatar asked Oct 03 '22 01:10

Dark Falcon


1 Answers

This is a problem of dependent name lookup.

In the expression overloaded(value);, the name overloaded is dependent as per [temp.dep]/1.

As far as I know, in the expression n::overloaded(value), the name overloaded (in the id-expression n::overloaded) is not dependent.


Dependent name lookup is very peculiar, [temp.dep.res]/1

In resolving dependent names, names from the following sources are considered:

  • Declarations that are visible at the point of definition of the template.
  • Declarations from namespaces associated with the types of the function arguments both from the instantiation context and from the definition context.

(There's a point of instantiation for function templates at the end of the file, so all declarations from associated namespaces can be found.)

For non-dependent names, the normal lookup rules apply (lookup from the definition context).

Therefore, to find names declared after the definition of the template, they have to be dependent and be found via ADL.


A simple workaround is to introduce another parameter to the function overloaded or wrap the argument, such that one of the arguments of this function has namespace n associated:

#include <iostream>

class C {};

namespace n {
  struct ADL_helper {};
  inline void overloaded(int, ADL_helper = {})
  { std::cout << "n::overloaded(int,..)" << std::endl; }
}

template<typename T> void try_it(T value) {
  overloaded(value, n::ADL_helper{});
}

namespace n {
  inline void overloaded(C, ADL_helper = {})
  { std::cout << "n::overloaded(C,..)" << std::endl; }
}

int main()
{
  try_it(1);
  C c;
  try_it(c);
}

alternatively:

namespace n {
  template < typename T >
  struct wrapper { T elem; };

  inline void overloaded(wrapper<int>)
  { std::cout << "n::overloaded(wrapper<int>)" << std::endl; }
}

template<typename T> void try_it(T value) {
  overloaded(n::wrapper<T>{value});
}

namespace n {
  inline void overloaded(wrapper<C>)
  { std::cout << "n::overloaded(wrapper<C>)" << std::endl; }
}
like image 128
dyp Avatar answered Oct 05 '22 23:10

dyp