c++templatesoverloadingoverload-resolution

Template function overloading behavior changes with function declaration order


I have the following C++ code which outputs 12:

#include <iostream>
using namespace std;

template<typename T>
T foo(T val) {
  return val;
}

template<typename T>
T bar(T n) {
  return foo(n);
}

int foo(int val) {
  return 1;
}

int main() {
  cout<<foo(2)<<bar(2)<<endl;
  return 0;
}

If I change the definition of bar to

int bar(int n) {
  return foo(n);
}

without changing its position, the output is still 12.

However, if I move the definitions of bar to just before main, the output changes to 11. (compiler: g++ 14.1.1)

My question is:

  1. Why does bar(2) call the template version of foo instead of the non-template overload even if both of them seem to be viable when bar is instantiated?
  2. How can I modify the code to output 11 without changing the order of function definitions while maintaining the generality of the template foo definition?

A follow-up question:

If foo is independent and looked up at the first stage, why should the following code compile?

#include <iostream>
using namespace std;

struct C {};

template<typename T>
T bar(T n) {
  return foo(n);
}

C foo(C val) {
  std::cout << "foo\n";
  return {};
}

int main() {
  bar(C());
  return 0;
}

Solution

  • Why does bar(2) call the template version of foo

    In the original case, at the point where foo(n) is called, only the function template version of foo is viable. Even though there is two-phase lookup, there is no associated namespace for int as int is a built in type. That is, the non-templated version is not added to the candidate list here.

    If you were to have a class type parameter instead of int, the call to foo(n) in the original case would've called the non-templated foo (demo), because this time through, argument dependent lookup in phase two would have seen the non-template foo is also a viable candidate.


    In the second case, when you move the definition of bar to just before main, both versions (templated and non-template) are visible for foo(n). And since a non-template version is preferred over the templated version, we see 11.


    How can I modify the code to output 11 without changing the order of function definitions while maintaining the generality of the template foo definition?

    You can add a forward-declaration for the non-templated foo before bar, as shown below:

    #include <iostream>
    using namespace std;
    
    template<typename T>
    T foo(T val) {
      return val;
    }
    
    int foo(int val);     //added this declaration
    
    template<typename T>
    T bar(T n) {
      return foo(n);
    }
    
    int foo(int val) {
      return 1;
    }
    
    int main() {
      cout<<foo(2)<<bar(2)<<endl; //now this prints 11
      return 0;
    }